X-Git-Url: http://git.samba.org/?p=amitay%2Fsamba.git;a=blobdiff_plain;f=source3%2Fprinting%2Fprinting.c;h=91ea09b28f0e668ff46bcb68c24b9abf5b836d9b;hp=9f2c08629d6e5749afbd3eafc343108bf796e236;hb=a4c27786ffe3b9513a0e64ce25f37dfe0ff0dc1b;hpb=aaa27706664da2855c09da0691c3717b571edaba diff --git a/source3/printing/printing.c b/source3/printing/printing.c index 9f2c08629d6..91ea09b28f0 100644 --- a/source3/printing/printing.c +++ b/source3/printing/printing.c @@ -1,39 +1,41 @@ -/* +/* Unix SMB/Netbios implementation. Version 3.0 printing backend routines Copyright (C) Andrew Tridgell 1992-2000 Copyright (C) Jeremy Allison 2002 - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "includes.h" #include "printing.h" +#include "librpc/gen_ndr/messaging.h" +#include "../librpc/gen_ndr/ndr_spoolss.h" +#include "nt_printing.h" +#include "../librpc/gen_ndr/netlogon.h" -extern SIG_ATOMIC_T got_sig_term; -extern SIG_ATOMIC_T reload_after_sighup; extern struct current_user current_user; extern userdom_struct current_user_info; /* Current printer interface */ static bool remove_from_jobs_changed(const char* sharename, uint32 jobid); -/* +/* the printing backend revolves around a tdb database that stores the - SMB view of the print queue - + SMB view of the print queue + The key for this database is a jobid - a internally generated number that uniquely identifies a print job @@ -41,7 +43,7 @@ static bool remove_from_jobs_changed(const char* sharename, uint32 jobid); - possibly running lpq and updating the internal database from that - reading entries from the database - jobids are assigned when a job starts spooling. + jobids are assigned when a job starts spooling. */ static TDB_CONTEXT *rap_tdb; @@ -116,10 +118,12 @@ bool rap_to_pjobid(uint16 rap_jobid, fstring sharename, uint32 *pjobid) key.dptr = buf; key.dsize = sizeof(rap_jobid); data = tdb_fetch(rap_tdb, key); - if ( data.dptr && data.dsize == sizeof(struct rap_jobid_key) ) + if ( data.dptr && data.dsize == sizeof(struct rap_jobid_key) ) { struct rap_jobid_key *jinfo = (struct rap_jobid_key*)data.dptr; - fstrcpy( sharename, jinfo->sharename ); + if (sharename != NULL) { + fstrcpy( sharename, jinfo->sharename ); + } *pjobid = jinfo->jobid; DEBUG(10,("rap_to_pjobid: jobid %u maps to RAP jobid %u\n", (unsigned int)*pjobid, (unsigned int)rap_jobid)); @@ -133,7 +137,7 @@ bool rap_to_pjobid(uint16 rap_jobid, fstring sharename, uint32 *pjobid) return False; } -static void rap_jobid_delete(const char* sharename, uint32 jobid) +void rap_jobid_delete(const char* sharename, uint32 jobid) { TDB_DATA key, data; uint16 rap_jobid; @@ -183,8 +187,8 @@ bool print_backend_init(struct messaging_context *msg_ctx) int services = lp_numservices(); int snum; - unlink(lock_path("printing.tdb")); - mkdir(lock_path("printing"),0755); + unlink(cache_path("printing.tdb")); + mkdir(cache_path("printing"),0755); /* handle a Samba upgrade */ @@ -202,7 +206,7 @@ bool print_backend_init(struct messaging_context *msg_ctx) return False; } if (tdb_fetch_int32(pdb->tdb, sversion) != PRINT_DATABASE_VERSION) { - tdb_traverse(pdb->tdb, tdb_traverse_delete_fn, NULL); + tdb_wipe_all(pdb->tdb); tdb_store_int32(pdb->tdb, sversion, PRINT_DATABASE_VERSION); } tdb_unlock_bystring(pdb->tdb, sversion); @@ -225,10 +229,10 @@ void printing_end(void) } /**************************************************************************** - Retrieve the set of printing functions for a given service. This allows + Retrieve the set of printing functions for a given service. This allows us to set the printer function table based on the value of the 'printing' service parameter. - + Use the generic interface as the default and only use cups interface only when asked for (and only when supported) ****************************************************************************/ @@ -250,7 +254,7 @@ static struct printif *get_printer_fns_from_type( enum printing_types type ) #endif /* HAVE_IPRINT */ printer_fns->type = type; - + return printer_fns; } @@ -274,10 +278,93 @@ static TDB_DATA print_key(uint32 jobid, uint32 *tmp) return ret; } +/**************************************************************************** + Pack the devicemode to store it in a tdb. +****************************************************************************/ +static int pack_devicemode(struct spoolss_DeviceMode *devmode, uint8 *buf, int buflen) +{ + enum ndr_err_code ndr_err; + DATA_BLOB blob; + int len = 0; + + if (devmode) { + ndr_err = ndr_push_struct_blob(&blob, talloc_tos(), + devmode, + (ndr_push_flags_fn_t) + ndr_push_spoolss_DeviceMode); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + DEBUG(10, ("pack_devicemode: " + "error encoding spoolss_DeviceMode\n")); + goto done; + } + } else { + ZERO_STRUCT(blob); + } + + len = tdb_pack(buf, buflen, "B", blob.length, blob.data); + + if (devmode) { + DEBUG(8, ("Packed devicemode [%s]\n", devmode->formname)); + } + +done: + return len; +} + +/**************************************************************************** + Unpack the devicemode to store it in a tdb. +****************************************************************************/ +static int unpack_devicemode(TALLOC_CTX *mem_ctx, + const uint8 *buf, int buflen, + struct spoolss_DeviceMode **devmode) +{ + struct spoolss_DeviceMode *dm; + enum ndr_err_code ndr_err; + char *data = NULL; + int data_len = 0; + DATA_BLOB blob; + int len = 0; + + *devmode = NULL; + + len = tdb_unpack(buf, buflen, "B", &data_len, &data); + if (!data) { + return len; + } + + dm = talloc_zero(mem_ctx, struct spoolss_DeviceMode); + if (!dm) { + goto done; + } + + blob = data_blob_const(data, data_len); + + ndr_err = ndr_pull_struct_blob(&blob, dm, dm, + (ndr_pull_flags_fn_t)ndr_pull_spoolss_DeviceMode); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + DEBUG(10, ("unpack_devicemode: " + "error parsing spoolss_DeviceMode\n")); + goto done; + } + + DEBUG(8, ("Unpacked devicemode [%s](%s)\n", + dm->devicename, dm->formname)); + if (dm->driverextra_data.data) { + DEBUG(8, ("with a private section of %d bytes\n", + dm->__driverextra_length)); + } + + *devmode = dm; + +done: + SAFE_FREE(data); + return len; +} + /*********************************************************************** - unpack a pjob from a tdb buffer + unpack a pjob from a tdb buffer ***********************************************************************/ - + int unpack_pjob( uint8 *buf, int buflen, struct printjob *pjob ) { int len = 0; @@ -287,7 +374,7 @@ int unpack_pjob( uint8 *buf, int buflen, struct printjob *pjob ) if ( !buf || !pjob ) return -1; - + len += tdb_unpack(buf+len, buflen-len, "dddddddddffff", &pjpid, &pjsysjob, @@ -302,13 +389,15 @@ int unpack_pjob( uint8 *buf, int buflen, struct printjob *pjob ) pjob->jobname, pjob->user, pjob->queuename); - + if ( len == -1 ) return -1; - - if ( (used = unpack_devicemode(&pjob->nt_devmode, buf+len, buflen-len)) == -1 ) + + used = unpack_devicemode(NULL, buf+len, buflen-len, &pjob->devmode); + if (used == -1) { return -1; - + } + len += used; pjob->pid = pjpid; @@ -320,7 +409,7 @@ int unpack_pjob( uint8 *buf, int buflen, struct printjob *pjob ) pjob->page_count = pjpage_count; pjob->spooled = pjspooled; pjob->smbjob = pjsmbjob; - + return len; } @@ -335,7 +424,7 @@ static struct printjob *print_job_find(const char *sharename, uint32 jobid) uint32_t tmp; TDB_DATA ret; struct tdb_print_db *pdb = get_print_db_byname(sharename); - + DEBUG(10,("print_job_find: looking up job %u for share %s\n", (unsigned int)jobid, sharename )); @@ -350,19 +439,17 @@ static struct printjob *print_job_find(const char *sharename, uint32 jobid) DEBUG(10,("print_job_find: failed to find jobid %u.\n", (unsigned int)jobid )); return NULL; } - - if ( pjob.nt_devmode ) { - free_nt_devicemode( &pjob.nt_devmode ); - } - + + talloc_free(pjob.devmode); + ZERO_STRUCT( pjob ); - + if ( unpack_pjob( ret.dptr, ret.dsize, &pjob ) == -1 ) { DEBUG(10,("print_job_find: failed to unpack jobid %u.\n", (unsigned int)jobid )); SAFE_FREE(ret.dptr); return NULL; } - + SAFE_FREE(ret.dptr); DEBUG(10,("print_job_find: returning system job %d for jobid %u.\n", @@ -449,7 +536,7 @@ static const struct { { LPQ_PAPEROUT, JOB_STATUS_PAPEROUT }, { LPQ_PRINTED, JOB_STATUS_PRINTED }, { LPQ_DELETED, JOB_STATUS_DELETED }, - { LPQ_BLOCKED, JOB_STATUS_BLOCKED }, + { LPQ_BLOCKED, JOB_STATUS_BLOCKED_DEVQ }, { LPQ_USER_INTERVENTION, JOB_STATUS_USER_INTERVENTION }, { -1, 0 } }; @@ -481,13 +568,13 @@ static void pjob_store_notify(const char* sharename, uint32 jobid, struct printj /* Job attributes that can't be changed. We only send notification for these on a new job. */ - /* ACHTUNG! Due to a bug in Samba's spoolss parsing of the - NOTIFY_INFO_DATA buffer, we *have* to send the job submission - time first or else we'll end up with potential alignment - errors. I don't think the systemtime should be spooled as - a string, but this gets us around that error. - --jerry (i'll feel dirty for this) */ - + /* ACHTUNG! Due to a bug in Samba's spoolss parsing of the + NOTIFY_INFO_DATA buffer, we *have* to send the job submission + time first or else we'll end up with potential alignment + errors. I don't think the systemtime should be spooled as + a string, but this gets us around that error. + --jerry (i'll feel dirty for this) */ + if (new_job) { notify_job_submitted(sharename, jobid, new_data->starttime); notify_job_username(sharename, jobid, new_data->user); @@ -524,7 +611,7 @@ static bool pjob_store(const char* sharename, uint32 jobid, struct printjob *pjo struct tdb_print_db *pdb = get_print_db_byname(sharename); uint8 *buf = NULL; int len, newlen, buflen; - + if (!pdb) return False; @@ -536,7 +623,7 @@ static bool pjob_store(const char* sharename, uint32 jobid, struct printjob *pjo /* Doh! Now we have to pack/unpack data since the NT_DEVICEMODE was added */ newlen = 0; - + do { len = 0; buflen = newlen; @@ -555,8 +642,8 @@ static bool pjob_store(const char* sharename, uint32 jobid, struct printjob *pjo pjob->user, pjob->queuename); - len += pack_devicemode(pjob->nt_devmode, buf+len, buflen-len); - + len += pack_devicemode(pjob->devmode, buf+len, buflen-len); + if (buflen != len) { buf = (uint8 *)SMB_REALLOC(buf, len); if (!buf) { @@ -566,8 +653,8 @@ static bool pjob_store(const char* sharename, uint32 jobid, struct printjob *pjo newlen = len; } } while ( buflen != len ); - - + + /* Store new data */ new_data.dptr = buf; @@ -587,7 +674,7 @@ static bool pjob_store(const char* sharename, uint32 jobid, struct printjob *pjo if ( unpack_pjob( old_data.dptr, old_data.dsize, &old_pjob ) != -1 ) { pjob_store_notify( sharename, jobid, &old_pjob , pjob ); - free_nt_devicemode( &old_pjob.nt_devmode ); + talloc_free(old_pjob.devmode); } } else { @@ -631,10 +718,10 @@ void pjob_delete(const char* sharename, uint32 jobid) /* We must cycle through JOB_STATUS_DELETING and JOB_STATUS_DELETED for the port monitor to delete the job properly. */ - + job_status = JOB_STATUS_DELETING|JOB_STATUS_DELETED; notify_job_status(sharename, jobid, job_status); - + /* Remove from printing.tdb */ tdb_delete(pdb->tdb, print_key(jobid, &tmp)); @@ -643,25 +730,6 @@ void pjob_delete(const char* sharename, uint32 jobid) rap_jobid_delete(sharename, jobid); } -/**************************************************************************** - Parse a file name from the system spooler to generate a jobid. -****************************************************************************/ - -static uint32 print_parse_jobid(char *fname) -{ - int jobid; - - if (strncmp(fname,PRINT_SPOOL_PREFIX,strlen(PRINT_SPOOL_PREFIX)) != 0) - return (uint32)-1; - fname += strlen(PRINT_SPOOL_PREFIX); - - jobid = atoi(fname); - if (jobid <= 0) - return (uint32)-1; - - return (uint32)jobid; -} - /**************************************************************************** List a unix job in the print database. ****************************************************************************/ @@ -670,7 +738,7 @@ static void print_unix_job(const char *sharename, print_queue_struct *q, uint32 { struct printjob pj, *old_pj; - if (jobid == (uint32)-1) + if (jobid == (uint32)-1) jobid = q->job + UNIX_JOB_START; /* Preserve the timestamp on an existing unix print job */ @@ -723,11 +791,11 @@ static int traverse_fn_delete(TDB_CONTEXT *t, TDB_DATA key, TDB_DATA data, void if ( key.dsize != sizeof(jobid) ) return 0; - + jobid = IVAL(key.dptr, 0); if ( unpack_pjob( data.dptr, data.dsize, &pjob ) == -1 ) return 0; - free_nt_devicemode( &pjob.nt_devmode ); + talloc_free(pjob.devmode); if (!pjob.smbjob) { @@ -743,7 +811,7 @@ static int traverse_fn_delete(TDB_CONTEXT *t, TDB_DATA key, TDB_DATA data, void (unsigned int)jobid )); pjob_delete(ts->sharename, jobid); return 0; - } + } /* need to continue the the bottom of the function to save the correct attributes */ @@ -764,7 +832,7 @@ static int traverse_fn_delete(TDB_CONTEXT *t, TDB_DATA key, TDB_DATA data, void } /* this check only makes sense for jobs submitted from Windows clients */ - + if ( pjob.smbjob ) { for (i=0;iqcount;i++) { uint32 curr_jobid; @@ -781,7 +849,7 @@ static int traverse_fn_delete(TDB_CONTEXT *t, TDB_DATA key, TDB_DATA data, void if ( pjob.status == LPQ_DELETING ) { int result; - result = (*(ts->print_if->job_delete))( + result = (*(ts->print_if->job_delete))( ts->sharename, ts->lprm_command, &pjob ); if ( result != 0 ) { @@ -794,14 +862,14 @@ static int traverse_fn_delete(TDB_CONTEXT *t, TDB_DATA key, TDB_DATA data, void pjob_delete(ts->sharename, jobid); pjob.status = LPQ_DELETED; } - + } break; } } } - + /* The job isn't in the system queue - we have to assume it has completed, so delete the database entry. */ @@ -825,11 +893,11 @@ static int traverse_fn_delete(TDB_CONTEXT *t, TDB_DATA key, TDB_DATA data, void return 0; } - /* Save the pjob attributes we will store. - FIXME!!! This is the only place where queue->job + /* Save the pjob attributes we will store. + FIXME!!! This is the only place where queue->job represents the SMB jobid --jerry */ - ts->queue[i].job = jobid; + ts->queue[i].job = jobid; ts->queue[i].size = pjob.size; ts->queue[i].page_count = pjob.page_count; ts->queue[i].status = pjob.status; @@ -903,7 +971,7 @@ static void set_updating_pid(const fstring sharename, bool updating) TDB_DATA data; pid_t updating_pid = sys_getpid(); uint8 buffer[4]; - + struct tdb_print_db *pdb = get_print_db_byname(sharename); if (!pdb) @@ -911,8 +979,8 @@ static void set_updating_pid(const fstring sharename, bool updating) slprintf(keystr, sizeof(keystr)-1, "UPDATING/%s", sharename); key = string_tdb_data(keystr); - - DEBUG(5, ("set_updating_pid: %s updating lpq cache for print share %s\n", + + DEBUG(5, ("set_updating_pid: %s updating lpq cache for print share %s\n", updating ? "" : "not ", sharename )); @@ -921,12 +989,12 @@ static void set_updating_pid(const fstring sharename, bool updating) release_print_db(pdb); return; } - + SIVAL( buffer, 0, updating_pid); data.dptr = buffer; data.dsize = 4; /* we always assume this is a 4 byte value */ - tdb_store(pdb->tdb, key, data, TDB_REPLACE); + tdb_store(pdb->tdb, key, data, TDB_REPLACE); release_print_db(pdb); } @@ -1072,36 +1140,36 @@ static bool print_cache_expired(const char *sharename, bool check_pending) * that last lpq scan would stay around for a loooong loooong time... :-). JRA. */ - if (last_qscan_time == ((time_t)-1) - || (time_now - last_qscan_time) >= lp_lpqcachetime() - || last_qscan_time > (time_now + MAX_CACHE_VALID_TIME)) + if (last_qscan_time == ((time_t)-1) + || (time_now - last_qscan_time) >= lp_lpqcachetime() + || last_qscan_time > (time_now + MAX_CACHE_VALID_TIME)) { uint32 u; time_t msg_pending_time; - DEBUG(4, ("print_cache_expired: cache expired for queue %s " - "(last_qscan_time = %d, time now = %d, qcachetime = %d)\n", - sharename, (int)last_qscan_time, (int)time_now, + DEBUG(4, ("print_cache_expired: cache expired for queue %s " + "(last_qscan_time = %d, time now = %d, qcachetime = %d)\n", + sharename, (int)last_qscan_time, (int)time_now, (int)lp_lpqcachetime() )); - /* check if another smbd has already sent a message to update the - queue. Give the pending message one minute to clear and - then send another message anyways. Make sure to check for + /* check if another smbd has already sent a message to update the + queue. Give the pending message one minute to clear and + then send another message anyways. Make sure to check for clocks that have been run forward and then back again. */ snprintf(key, sizeof(key), "MSG_PENDING/%s", sharename); - if ( check_pending - && tdb_fetch_uint32( pdb->tdb, key, &u ) + if ( check_pending + && tdb_fetch_uint32( pdb->tdb, key, &u ) && (msg_pending_time=u) > 0 - && msg_pending_time <= time_now - && (time_now - msg_pending_time) < 60 ) + && msg_pending_time <= time_now + && (time_now - msg_pending_time) < 60 ) { DEBUG(4,("print_cache_expired: message already pending for %s. Accepting cache\n", sharename)); goto done; } - + result = True; } @@ -1114,7 +1182,7 @@ done: main work for updating the lpq cahe for a printer queue ****************************************************************************/ -static void print_queue_update_internal( const char *sharename, +static void print_queue_update_internal( const char *sharename, struct printif *current_printif, char *lpq_command, char *lprm_command ) { @@ -1132,7 +1200,7 @@ static void print_queue_update_internal( const char *sharename, if (!pdb) { return; } - + DEBUG(5,("print_queue_update_internal: printer = %s, type = %d, lpq command = [%s]\n", sharename, current_printif->type, lpq_command)); @@ -1141,32 +1209,31 @@ static void print_queue_update_internal( const char *sharename, * attempting to get the lock and doing this * if the lpq takes a long time. */ - + slprintf(cachestr, sizeof(cachestr)-1, "CACHE/%s", sharename); tdb_store_int32(pdb->tdb, cachestr, (int)time(NULL)); /* get the current queue using the appropriate interface */ ZERO_STRUCT(status); - qcount = (*(current_printif->queue_get))(sharename, - current_printif->type, + qcount = (*(current_printif->queue_get))(sharename, + current_printif->type, lpq_command, &queue, &status); - DEBUG(3, ("print_queue_update_internal: %d job%s in queue for %s\n", + DEBUG(3, ("print_queue_update_internal: %d job%s in queue for %s\n", qcount, (qcount != 1) ? "s" : "", sharename)); /* Sort the queue by submission time otherwise they are displayed in hash order. */ - qsort(queue, qcount, sizeof(print_queue_struct), - QSORT_CAST(printjob_comp)); + TYPESAFE_QSORT(queue, qcount, printjob_comp); /* any job in the internal database that is marked as spooled and doesn't exist in the system queue is considered finished and removed from the database - any job in the system database but not in the internal database + any job in the system database but not in the internal database is added as a unix job fill in any system job numbers as we go @@ -1242,7 +1309,7 @@ static void print_queue_update_internal( const char *sharename, status.qcount = qcount; data.dptr = (uint8 *)&status; data.dsize = sizeof(status); - tdb_store(pdb->tdb, key, data, TDB_REPLACE); + tdb_store(pdb->tdb, key, data, TDB_REPLACE); /* * Update the cache time again. We want to do this call @@ -1274,7 +1341,7 @@ static void print_queue_update_internal( const char *sharename, smbd processes maytry to update the lpq cache concurrently). ****************************************************************************/ -static void print_queue_update_with_lock( const char *sharename, +static void print_queue_update_with_lock( const char *sharename, struct printif *current_printif, char *lpq_command, char *lprm_command ) { @@ -1291,7 +1358,7 @@ static void print_queue_update_with_lock( const char *sharename, release_print_db(pdb); return; } - + /* * Check to see if someone else is doing this update. * This is essentially a mutex on the update. @@ -1342,10 +1409,10 @@ static void print_queue_update_with_lock( const char *sharename, tdb_unlock_bystring(pdb->tdb, keystr); /* do the main work now */ - - print_queue_update_internal( sharename, current_printif, + + print_queue_update_internal( sharename, current_printif, lpq_command, lprm_command ); - + /* Delete our pid from the db. */ set_updating_pid(sharename, False); release_print_db(pdb); @@ -1378,7 +1445,7 @@ static void print_queue_receive(struct messaging_context *msg, return; } - print_queue_update_with_lock(sharename, + print_queue_update_with_lock(sharename, get_printer_fns_from_type((enum printing_types)printing_type), lpqcommand, lprmcommand ); @@ -1387,14 +1454,56 @@ static void print_queue_receive(struct messaging_context *msg, return; } +static void printing_pause_fd_handler(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, + void *private_data) +{ + /* + * If pause_pipe[1] is closed it means the parent smbd + * and children exited or aborted. + */ + exit_server_cleanly(NULL); +} + +extern struct child_pid *children; +extern int num_children; + +static void add_child_pid(pid_t pid) +{ + struct child_pid *child; + + child = SMB_MALLOC_P(struct child_pid); + if (child == NULL) { + DEBUG(0, ("Could not add child struct -- malloc failed\n")); + return; + } + child->pid = pid; + DLIST_ADD(children, child); + num_children += 1; +} + static pid_t background_lpq_updater_pid = -1; /**************************************************************************** main thread of the background lpq updater ****************************************************************************/ -void start_background_queue(void) +void start_background_queue(struct tevent_context *ev, + struct messaging_context *msg_ctx) { + /* Use local variables for this as we don't + * need to save the parent side of this, just + * ensure it closes when the process exits. + */ + int pause_pipe[2]; + DEBUG(3,("start_background_queue: Starting background LPQ thread\n")); + + if (pipe(pause_pipe) == -1) { + DEBUG(5,("start_background_queue: cannot create pipe. %s\n", strerror(errno) )); + exit(1); + } + background_lpq_updater_pid = sys_fork(); if (background_lpq_updater_pid == -1) { @@ -1402,55 +1511,68 @@ void start_background_queue(void) exit(1); } + /* Track the printing pid along with other smbd children */ + add_child_pid(background_lpq_updater_pid); + if(background_lpq_updater_pid == 0) { + struct tevent_fd *fde; + int ret; + NTSTATUS status; + /* Child. */ DEBUG(5,("start_background_queue: background LPQ thread started\n")); - claim_connection( NULL, "smbd lpq backend", - FLAG_MSG_GENERAL|FLAG_MSG_SMBD|FLAG_MSG_PRINT_GENERAL); + close(pause_pipe[0]); + pause_pipe[0] = -1; + + status = reinit_after_fork(msg_ctx, ev, procid_self(), true); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("reinit_after_fork() failed\n")); + smb_panic("reinit_after_fork() failed"); + } + + smbd_setup_sig_term_handler(); + smbd_setup_sig_hup_handler(ev, msg_ctx); + + if (!serverid_register(procid_self(), + FLAG_MSG_GENERAL|FLAG_MSG_SMBD + |FLAG_MSG_PRINT_GENERAL)) { + exit(1); + } if (!locking_init()) { exit(1); } - messaging_register(smbd_messaging_context(), NULL, - MSG_PRINTER_UPDATE, print_queue_receive); - - DEBUG(5,("start_background_queue: background LPQ thread waiting for messages\n")); - while (1) { - pause(); - - /* check for some essential signals first */ - - if (got_sig_term) { - exit_server_cleanly(NULL); - } - - if (reload_after_sighup) { - change_to_root_user(); - DEBUG(1,("Reloading services after SIGHUP\n")); - reload_services(False); - reload_after_sighup = 0; - } - - /* now check for messages */ - - DEBUG(10,("start_background_queue: background LPQ thread got a message\n")); - message_dispatch(smbd_messaging_context()); - - /* process any pending print change notify messages */ - - print_notify_send_messages(smbd_messaging_context(), - 0); + messaging_register(msg_ctx, NULL, MSG_PRINTER_UPDATE, + print_queue_receive); + + fde = tevent_add_fd(ev, ev, pause_pipe[1], TEVENT_FD_READ, + printing_pause_fd_handler, + NULL); + if (!fde) { + DEBUG(0,("tevent_add_fd() failed for pause_pipe\n")); + smb_panic("tevent_add_fd() failed for pause_pipe"); } + + DEBUG(5,("start_background_queue: background LPQ thread waiting for messages\n")); + ret = tevent_loop_wait(ev); + /* should not be reached */ + DEBUG(0,("background_queue: tevent_loop_wait() exited with %d - %s\n", + ret, (ret == 0) ? "out of events" : strerror(errno))); + exit(1); } + + close(pause_pipe[1]); } /**************************************************************************** update the internal database from the system print queue for a queue ****************************************************************************/ -static void print_queue_update(int snum, bool force) +static void print_queue_update(struct messaging_context *msg_ctx, + int snum, bool force) { fstring key; fstring sharename; @@ -1471,7 +1593,7 @@ static void print_queue_update(int snum, bool force) lpqcommand = talloc_string_sub2(ctx, lp_lpqcommand(snum), "%p", - PRINTERNAME(snum), + lp_printername(snum), false, false, false); if (!lpqcommand) { return; @@ -1491,7 +1613,7 @@ static void print_queue_update(int snum, bool force) lprmcommand = talloc_string_sub2(ctx, lp_lprmcommand(snum), "%p", - PRINTERNAME(snum), + lp_printername(snum), false, false, false); if (!lprmcommand) { return; @@ -1543,10 +1665,10 @@ static void print_queue_update(int snum, bool force) SMB_ASSERT( newlen == len ); DEBUG(10,("print_queue_update: Sending message -> printer = %s, " - "type = %d, lpq command = [%s] lprm command = [%s]\n", + "type = %d, lpq command = [%s] lprm command = [%s]\n", sharename, type, lpqcommand, lprmcommand )); - /* here we set a msg pending record for other smbd processes + /* here we set a msg pending record for other smbd processes to throttle the number of duplicate print_queue_update msgs sent. */ @@ -1568,9 +1690,8 @@ static void print_queue_update(int snum, bool force) release_print_db( pdb ); /* finally send the message */ - - messaging_send_buf(smbd_messaging_context(), - pid_to_procid(background_lpq_updater_pid), + + messaging_send_buf(msg_ctx, pid_to_procid(background_lpq_updater_pid), MSG_PRINTER_UPDATE, (uint8 *)buffer, len); SAFE_FREE( buffer ); @@ -1580,7 +1701,7 @@ static void print_queue_update(int snum, bool force) /**************************************************************************** Create/Update an entry in the print tdb that will allow us to send notify - updates only to interested smbd's. + updates only to interested smbd's. ****************************************************************************/ bool print_notify_register_pid(int snum) @@ -1596,17 +1717,17 @@ bool print_notify_register_pid(int snum) /* if (snum == -1), then the change notify request was on a print server handle and we need to register on all print queus */ - - if (snum == -1) + + if (snum == -1) { int num_services = lp_numservices(); int idx; - + for ( idx=0; idxpid != sys_getpid()) - return -1; - return pjob->fd; -} - /**************************************************************************** Give the filename used for a jobid. Only valid for the process doing the spooling and when the job @@ -1812,31 +1918,21 @@ char *print_job_fname(const char* sharename, uint32 jobid) has not been spooled. ****************************************************************************/ -NT_DEVICEMODE *print_job_devmode(const char* sharename, uint32 jobid) +struct spoolss_DeviceMode *print_job_devmode(const char* sharename, uint32 jobid) { struct printjob *pjob = print_job_find(sharename, jobid); - + if ( !pjob ) return NULL; - - return pjob->nt_devmode; -} - -/**************************************************************************** - Set the place in the queue for a job. -****************************************************************************/ -bool print_job_set_place(const char *sharename, uint32 jobid, int place) -{ - DEBUG(2,("print_job_set_place not implemented yet\n")); - return False; + return pjob->devmode; } /**************************************************************************** Set the name of a job. Only possible for owner. ****************************************************************************/ -bool print_job_set_name(const char *sharename, uint32 jobid, char *name) +bool print_job_set_name(const char *sharename, uint32 jobid, const char *name) { struct printjob *pjob; @@ -1848,6 +1944,28 @@ bool print_job_set_name(const char *sharename, uint32 jobid, char *name) return pjob_store(sharename, jobid, pjob); } +/**************************************************************************** + Get the name of a job. Only possible for owner. +****************************************************************************/ + +bool print_job_get_name(TALLOC_CTX *mem_ctx, const char *sharename, uint32_t jobid, char **name) +{ + struct printjob *pjob; + + pjob = print_job_find(sharename, jobid); + if (!pjob || pjob->pid != sys_getpid()) { + return false; + } + + *name = talloc_strdup(mem_ctx, pjob->jobname); + if (!*name) { + return false; + } + + return true; +} + + /*************************************************************************** Remove a jobid from the 'jobs changed' list. ***************************************************************************/ @@ -1929,9 +2047,9 @@ static bool print_job_delete1(int snum, uint32 jobid) return True; /* Hrm - we need to be able to cope with deleting a job before it - has reached the spooler. Just mark it as LPQ_DELETING and + has reached the spooler. Just mark it as LPQ_DELETING and let the print_queue_update() code rmeove the record */ - + if (pjob->sysjob == -1) { DEBUG(5, ("attempt to delete job %u not seen by lpr\n", (unsigned int)jobid)); @@ -1942,11 +2060,11 @@ static bool print_job_delete1(int snum, uint32 jobid) pjob->status = LPQ_DELETING; pjob_store(sharename, jobid, pjob); - if (pjob->spooled && pjob->sysjob != -1) + if (pjob->spooled && pjob->sysjob != -1) { result = (*(current_printif->job_delete))( - PRINTERNAME(snum), - lp_lprmcommand(snum), + lp_printername(snum), + lp_lprmcommand(snum), pjob); /* Delete the tdb entry if the delete succeeded or the job hasn't @@ -1974,94 +2092,93 @@ static bool print_job_delete1(int snum, uint32 jobid) Return true if the current user owns the print job. ****************************************************************************/ -static bool is_owner(struct current_user *user, const char *servicename, +static bool is_owner(struct auth_serversupplied_info *server_info, + const char *servicename, uint32 jobid) { struct printjob *pjob = print_job_find(servicename, jobid); - user_struct *vuser; - if (!pjob || !user) + if (!pjob || !server_info) return False; - if ((vuser = get_valid_user_struct(user->vuid)) != NULL) { - return strequal(pjob->user, vuser->user.smb_name); - } else { - return strequal(pjob->user, uidtoname(user->ut.uid)); - } + return strequal(pjob->user, server_info->sanitized_username); } /**************************************************************************** Delete a print job. ****************************************************************************/ -bool print_job_delete(struct current_user *user, int snum, uint32 jobid, WERROR *errcode) +WERROR print_job_delete(struct auth_serversupplied_info *server_info, + struct messaging_context *msg_ctx, + int snum, uint32_t jobid) { - const char* sharename = lp_const_servicename( snum ); + const char* sharename = lp_const_servicename(snum); struct printjob *pjob; bool owner; char *fname; - *errcode = WERR_OK; - - owner = is_owner(user, lp_const_servicename(snum), jobid); - + owner = is_owner(server_info, lp_const_servicename(snum), jobid); + /* Check access against security descriptor or whether the user owns their job. */ - if (!owner && - !print_access_check(user, snum, JOB_ACCESS_ADMINISTER)) { + if (!owner && + !print_access_check(server_info, msg_ctx, snum, + JOB_ACCESS_ADMINISTER)) { DEBUG(3, ("delete denied by security descriptor\n")); - *errcode = WERR_ACCESS_DENIED; /* BEGIN_ADMIN_LOG */ - sys_adminlog( LOG_ERR, + sys_adminlog( LOG_ERR, "Permission denied-- user not allowed to delete, \ pause, or resume print job. User name: %s. Printer name: %s.", - uidtoname(user->ut.uid), PRINTERNAME(snum) ); + uidtoname(server_info->utok.uid), + lp_printername(snum) ); /* END_ADMIN_LOG */ - return False; + return WERR_ACCESS_DENIED; } - /* + /* * get the spooled filename of the print job * if this works, then the file has not been spooled - * to the underlying print system. Just delete the + * to the underlying print system. Just delete the * spool file & return. */ - - if ( (fname = print_job_fname( sharename, jobid )) != NULL ) - { + + fname = print_job_fname(sharename, jobid); + if (fname != NULL) { /* remove the spool file */ - DEBUG(10,("print_job_delete: Removing spool file [%s]\n", fname )); - if ( unlink( fname ) == -1 ) { - *errcode = map_werror_from_unix(errno); - return False; + DEBUG(10, ("print_job_delete: " + "Removing spool file [%s]\n", fname)); + if (unlink(fname) == -1) { + return map_werror_from_unix(errno); } } - + if (!print_job_delete1(snum, jobid)) { - *errcode = WERR_ACCESS_DENIED; - return False; + return WERR_ACCESS_DENIED; } /* force update the database and say the delete failed if the job still exists */ - print_queue_update(snum, True); - + print_queue_update(msg_ctx, snum, True); + pjob = print_job_find(sharename, jobid); - if ( pjob && (pjob->status != LPQ_DELETING) ) - *errcode = WERR_ACCESS_DENIED; + if (pjob && (pjob->status != LPQ_DELETING)) { + return WERR_ACCESS_DENIED; + } - return (pjob == NULL ); + return WERR_PRINTER_HAS_JOBS_QUEUED; } /**************************************************************************** Pause a job. ****************************************************************************/ -bool print_job_pause(struct current_user *user, int snum, uint32 jobid, WERROR *errcode) +bool print_job_pause(struct auth_serversupplied_info *server_info, + struct messaging_context *msg_ctx, + int snum, uint32 jobid, WERROR *errcode) { const char* sharename = lp_const_servicename(snum); struct printjob *pjob; @@ -2069,8 +2186,8 @@ bool print_job_pause(struct current_user *user, int snum, uint32 jobid, WERROR * struct printif *current_printif = get_printer_fns( snum ); pjob = print_job_find(sharename, jobid); - - if (!pjob || !user) { + + if (!pjob || !server_info) { DEBUG(10, ("print_job_pause: no pjob or user for jobid %u\n", (unsigned int)jobid )); return False; @@ -2082,15 +2199,17 @@ bool print_job_pause(struct current_user *user, int snum, uint32 jobid, WERROR * return False; } - if (!is_owner(user, lp_const_servicename(snum), jobid) && - !print_access_check(user, snum, JOB_ACCESS_ADMINISTER)) { + if (!is_owner(server_info, lp_const_servicename(snum), jobid) && + !print_access_check(server_info, msg_ctx, snum, + JOB_ACCESS_ADMINISTER)) { DEBUG(3, ("pause denied by security descriptor\n")); /* BEGIN_ADMIN_LOG */ - sys_adminlog( LOG_ERR, + sys_adminlog( LOG_ERR, "Permission denied-- user not allowed to delete, \ pause, or resume print job. User name: %s. Printer name: %s.", - uidtoname(user->ut.uid), PRINTERNAME(snum) ); + uidtoname(server_info->utok.uid), + lp_printername(snum) ); /* END_ADMIN_LOG */ *errcode = WERR_ACCESS_DENIED; @@ -2121,7 +2240,9 @@ pause, or resume print job. User name: %s. Printer name: %s.", Resume a job. ****************************************************************************/ -bool print_job_resume(struct current_user *user, int snum, uint32 jobid, WERROR *errcode) +bool print_job_resume(struct auth_serversupplied_info *server_info, + struct messaging_context *msg_ctx, + int snum, uint32 jobid, WERROR *errcode) { const char *sharename = lp_const_servicename(snum); struct printjob *pjob; @@ -2129,8 +2250,8 @@ bool print_job_resume(struct current_user *user, int snum, uint32 jobid, WERROR struct printif *current_printif = get_printer_fns( snum ); pjob = print_job_find(sharename, jobid); - - if (!pjob || !user) { + + if (!pjob || !server_info) { DEBUG(10, ("print_job_resume: no pjob or user for jobid %u\n", (unsigned int)jobid )); return False; @@ -2142,16 +2263,18 @@ bool print_job_resume(struct current_user *user, int snum, uint32 jobid, WERROR return False; } - if (!is_owner(user, lp_const_servicename(snum), jobid) && - !print_access_check(user, snum, JOB_ACCESS_ADMINISTER)) { + if (!is_owner(server_info, lp_const_servicename(snum), jobid) && + !print_access_check(server_info, msg_ctx, snum, + JOB_ACCESS_ADMINISTER)) { DEBUG(3, ("resume denied by security descriptor\n")); *errcode = WERR_ACCESS_DENIED; /* BEGIN_ADMIN_LOG */ - sys_adminlog( LOG_ERR, + sys_adminlog( LOG_ERR, "Permission denied-- user not allowed to delete, \ pause, or resume print job. User name: %s. Printer name: %s.", - uidtoname(user->ut.uid), PRINTERNAME(snum) ); + uidtoname(server_info->utok.uid), + lp_printername(snum) ); /* END_ADMIN_LOG */ return False; } @@ -2180,7 +2303,7 @@ pause, or resume print job. User name: %s. Printer name: %s.", ssize_t print_job_write(int snum, uint32 jobid, const char *buf, SMB_OFF_T pos, size_t size) { const char* sharename = lp_const_servicename(snum); - int return_code; + ssize_t return_code; struct printjob *pjob; pjob = print_job_find(sharename, jobid); @@ -2191,6 +2314,11 @@ ssize_t print_job_write(int snum, uint32 jobid, const char *buf, SMB_OFF_T pos, if (pjob->pid != sys_getpid()) return -1; + /* if SMBD is spooling this can't be allowed */ + if (pjob->status == PJOB_SMBD_SPOOLING) { + return -1; + } + return_code = write_data_at_offset(pjob->fd, buf, size, pos); if (return_code>0) { @@ -2223,7 +2351,7 @@ static int get_queue_status(const char* sharename, print_status_struct *status) data = tdb_fetch(pdb->tdb, string_tdb_data(keystr)); if (data.dptr) { if (data.dsize == sizeof(print_status_struct)) - /* this memcpy is ok since the status struct was + /* this memcpy is ok since the status struct was not packed before storing it in the tdb */ memcpy(status, data.dptr, sizeof(print_status_struct)); SAFE_FREE(data.dptr); @@ -2238,18 +2366,19 @@ static int get_queue_status(const char* sharename, print_status_struct *status) Determine the number of jobs in a queue. ****************************************************************************/ -int print_queue_length(int snum, print_status_struct *pstatus) +int print_queue_length(struct messaging_context *msg_ctx, int snum, + print_status_struct *pstatus) { const char* sharename = lp_const_servicename( snum ); print_status_struct status; int len; ZERO_STRUCT( status ); - + /* make sure the database is up to date */ if (print_cache_expired(lp_const_servicename(snum), True)) - print_queue_update(snum, False); - + print_queue_update(msg_ctx, snum, False); + /* also fetch the queue status */ memset(&status, 0, sizeof(status)); len = get_queue_status(sharename, &status); @@ -2264,50 +2393,72 @@ int print_queue_length(int snum, print_status_struct *pstatus) Allocate a jobid. Hold the lock for as short a time as possible. ***************************************************************************/ -static bool allocate_print_jobid(struct tdb_print_db *pdb, int snum, const char *sharename, uint32 *pjobid) +static WERROR allocate_print_jobid(struct tdb_print_db *pdb, int snum, + const char *sharename, uint32 *pjobid) { int i; uint32 jobid; + enum TDB_ERROR terr; + int ret; *pjobid = (uint32)-1; for (i = 0; i < 3; i++) { /* Lock the database - only wait 20 seconds. */ - if (tdb_lock_bystring_with_timeout(pdb->tdb, "INFO/nextjob", 20) == -1) { - DEBUG(0,("allocate_print_jobid: failed to lock printing database %s\n", sharename)); - return False; + ret = tdb_lock_bystring_with_timeout(pdb->tdb, + "INFO/nextjob", 20); + if (ret == -1) { + DEBUG(0, ("allocate_print_jobid: " + "Failed to lock printing database %s\n", + sharename)); + terr = tdb_error(pdb->tdb); + return ntstatus_to_werror(map_nt_error_from_tdb(terr)); } if (!tdb_fetch_uint32(pdb->tdb, "INFO/nextjob", &jobid)) { - if (tdb_error(pdb->tdb) != TDB_ERR_NOEXIST) { - DEBUG(0, ("allocate_print_jobid: failed to fetch INFO/nextjob for print queue %s\n", - sharename)); - return False; + terr = tdb_error(pdb->tdb); + if (terr != TDB_ERR_NOEXIST) { + DEBUG(0, ("allocate_print_jobid: " + "Failed to fetch INFO/nextjob " + "for print queue %s\n", sharename)); + tdb_unlock_bystring(pdb->tdb, "INFO/nextjob"); + return ntstatus_to_werror(map_nt_error_from_tdb(terr)); } + DEBUG(10, ("allocate_print_jobid: " + "No existing jobid in %s\n", sharename)); jobid = 0; } + DEBUG(10, ("allocate_print_jobid: " + "Read jobid %u from %s\n", jobid, sharename)); + jobid = NEXT_JOBID(jobid); - if (tdb_store_int32(pdb->tdb, "INFO/nextjob", jobid)==-1) { - DEBUG(3, ("allocate_print_jobid: failed to store INFO/nextjob.\n")); + ret = tdb_store_int32(pdb->tdb, "INFO/nextjob", jobid); + if (ret == -1) { + terr = tdb_error(pdb->tdb); + DEBUG(3, ("allocate_print_jobid: " + "Failed to store INFO/nextjob.\n")); tdb_unlock_bystring(pdb->tdb, "INFO/nextjob"); - return False; + return ntstatus_to_werror(map_nt_error_from_tdb(terr)); } /* We've finished with the INFO/nextjob lock. */ tdb_unlock_bystring(pdb->tdb, "INFO/nextjob"); - - if (!print_job_exists(sharename, jobid)) + + if (!print_job_exists(sharename, jobid)) { break; + } + DEBUG(10, ("allocate_print_jobid: " + "Found jobid %u in %s\n", jobid, sharename)); } if (i > 2) { - DEBUG(0, ("allocate_print_jobid: failed to allocate a print job for queue %s\n", - sharename)); + DEBUG(0, ("allocate_print_jobid: " + "Failed to allocate a print job for queue %s\n", + sharename)); /* Probably full... */ - errno = ENOSPC; - return False; + return WERR_NO_SPOOL_SPACE; } /* Store a dummy placeholder. */ @@ -2318,14 +2469,16 @@ static bool allocate_print_jobid(struct tdb_print_db *pdb, int snum, const char dum.dsize = 0; if (tdb_store(pdb->tdb, print_key(jobid, &tmp), dum, TDB_INSERT) == -1) { - DEBUG(3, ("allocate_print_jobid: jobid (%d) failed to store placeholder.\n", - jobid )); - return False; + DEBUG(3, ("allocate_print_jobid: " + "jobid (%d) failed to store placeholder.\n", + jobid )); + terr = tdb_error(pdb->tdb); + return ntstatus_to_werror(map_nt_error_from_tdb(terr)); } } *pjobid = jobid; - return True; + return WERR_OK; } /*************************************************************************** @@ -2347,78 +2500,175 @@ static bool add_to_jobs_changed(struct tdb_print_db *pdb, uint32 jobid) data) == 0); } + /*************************************************************************** - Start spooling a job - return the jobid. + Do all checks needed to determine if we can start a job. ***************************************************************************/ -uint32 print_job_start(struct current_user *user, int snum, char *jobname, NT_DEVICEMODE *nt_devmode ) +static WERROR print_job_checks(struct auth_serversupplied_info *server_info, + struct messaging_context *msg_ctx, + int snum, int *njobs) { - uint32 jobid; - char *path; - struct printjob pjob; - user_struct *vuser; const char *sharename = lp_const_servicename(snum); - struct tdb_print_db *pdb = get_print_db_byname(sharename); - int njobs; - - errno = 0; - - if (!pdb) - return (uint32)-1; + uint64_t dspace, dsize; + uint64_t minspace; + int ret; - if (!print_access_check(user, snum, PRINTER_ACCESS_USE)) { - DEBUG(3, ("print_job_start: job start denied by security descriptor\n")); - release_print_db(pdb); - return (uint32)-1; + if (!print_access_check(server_info, msg_ctx, snum, + PRINTER_ACCESS_USE)) { + DEBUG(3, ("print_job_checks: " + "job start denied by security descriptor\n")); + return WERR_ACCESS_DENIED; } - if (!print_time_access_check(lp_servicename(snum))) { - DEBUG(3, ("print_job_start: job start denied by time check\n")); - release_print_db(pdb); - return (uint32)-1; + if (!print_time_access_check(server_info, msg_ctx, sharename)) { + DEBUG(3, ("print_job_checks: " + "job start denied by time check\n")); + return WERR_ACCESS_DENIED; } - path = lp_pathname(snum); - /* see if we have sufficient disk space */ if (lp_minprintspace(snum)) { - SMB_BIG_UINT dspace, dsize; - if (sys_fsusage(path, &dspace, &dsize) == 0 && - dspace < 2*(SMB_BIG_UINT)lp_minprintspace(snum)) { - DEBUG(3, ("print_job_start: disk space check failed.\n")); - release_print_db(pdb); - errno = ENOSPC; - return (uint32)-1; + minspace = lp_minprintspace(snum); + ret = sys_fsusage(lp_pathname(snum), &dspace, &dsize); + if (ret == 0 && dspace < 2*minspace) { + DEBUG(3, ("print_job_checks: " + "disk space check failed.\n")); + return WERR_NO_SPOOL_SPACE; } } /* for autoloaded printers, check that the printcap entry still exists */ - if (lp_autoloaded(snum) && !pcap_printername_ok(lp_const_servicename(snum))) { - DEBUG(3, ("print_job_start: printer name %s check failed.\n", lp_const_servicename(snum) )); - release_print_db(pdb); - errno = ENOENT; - return (uint32)-1; + if (lp_autoloaded(snum) && !pcap_printername_ok(sharename)) { + DEBUG(3, ("print_job_checks: printer name %s check failed.\n", + sharename)); + return WERR_ACCESS_DENIED; } /* Insure the maximum queue size is not violated */ - if ((njobs = print_queue_length(snum,NULL)) > lp_maxprintjobs(snum)) { - DEBUG(3, ("print_job_start: Queue %s number of jobs (%d) larger than max printjobs per queue (%d).\n", - sharename, njobs, lp_maxprintjobs(snum) )); + *njobs = print_queue_length(msg_ctx, snum, NULL); + if (*njobs > lp_maxprintjobs(snum)) { + DEBUG(3, ("print_job_checks: Queue %s number of jobs (%d) " + "larger than max printjobs per queue (%d).\n", + sharename, *njobs, lp_maxprintjobs(snum))); + return WERR_NO_SPOOL_SPACE; + } + + return WERR_OK; +} + +/*************************************************************************** + Create a job file. +***************************************************************************/ + +static WERROR print_job_spool_file(int snum, uint32_t jobid, + const char *output_file, + struct printjob *pjob) +{ + WERROR werr; + SMB_STRUCT_STAT st; + const char *path; + int len; + + /* if this file is within the printer path, it means that smbd + * is spooling it and will pass us control when it is finished. + * Verify that the file name is ok, within path, and it is + * already already there */ + if (output_file) { + path = lp_pathname(snum); + len = strlen(path); + if (strncmp(output_file, path, len) == 0 && + (output_file[len - 1] == '/' || output_file[len] == '/')) { + + /* verify path is not too long */ + if (strlen(output_file) >= sizeof(pjob->filename)) { + return WERR_INVALID_NAME; + } + + /* verify that the file exists */ + if (sys_stat(output_file, &st, false) != 0) { + return WERR_INVALID_NAME; + } + + fstrcpy(pjob->filename, output_file); + + DEBUG(3, ("print_job_spool_file:" + "External spooling activated")); + + /* we do not open the file until spooling is done */ + pjob->fd = -1; + pjob->status = PJOB_SMBD_SPOOLING; + + return WERR_OK; + } + } + + slprintf(pjob->filename, sizeof(pjob->filename)-1, + "%s/%s%.8u.XXXXXX", lp_pathname(snum), + PRINT_SPOOL_PREFIX, (unsigned int)jobid); + pjob->fd = mkstemp(pjob->filename); + + if (pjob->fd == -1) { + werr = map_werror_from_unix(errno); + if (W_ERROR_EQUAL(werr, WERR_ACCESS_DENIED)) { + /* Common setup error, force a report. */ + DEBUG(0, ("print_job_spool_file: " + "insufficient permissions to open spool " + "file %s.\n", pjob->filename)); + } else { + /* Normal case, report at level 3 and above. */ + DEBUG(3, ("print_job_spool_file: " + "can't open spool file %s\n", + pjob->filename)); + } + return werr; + } + + return WERR_OK; +} + +/*************************************************************************** + Start spooling a job - return the jobid. +***************************************************************************/ + +WERROR print_job_start(struct auth_serversupplied_info *server_info, + struct messaging_context *msg_ctx, + int snum, const char *docname, const char *filename, + struct spoolss_DeviceMode *devmode, uint32_t *_jobid) +{ + uint32_t jobid; + char *path; + struct printjob pjob; + const char *sharename = lp_const_servicename(snum); + struct tdb_print_db *pdb = get_print_db_byname(sharename); + int njobs; + WERROR werr; + + if (!pdb) { + return WERR_INTERNAL_DB_CORRUPTION; + } + + path = lp_pathname(snum); + + werr = print_job_checks(server_info, msg_ctx, snum, &njobs); + if (!W_ERROR_IS_OK(werr)) { release_print_db(pdb); - errno = ENOSPC; - return (uint32)-1; + return werr; } - DEBUG(10,("print_job_start: Queue %s number of jobs (%d), max printjobs = %d\n", - sharename, njobs, lp_maxprintjobs(snum) )); + DEBUG(10, ("print_job_start: " + "Queue %s number of jobs (%d), max printjobs = %d\n", + sharename, njobs, lp_maxprintjobs(snum))); - if (!allocate_print_jobid(pdb, snum, sharename, &jobid)) + werr = allocate_print_jobid(pdb, snum, sharename, &jobid); + if (!W_ERROR_IS_OK(werr)) { goto fail; + } /* create the database entry */ - + ZERO_STRUCT(pjob); - + pjob.pid = sys_getpid(); pjob.sysjob = -1; pjob.fd = -1; @@ -2427,37 +2677,24 @@ uint32 print_job_start(struct current_user *user, int snum, char *jobname, NT_DE pjob.size = 0; pjob.spooled = False; pjob.smbjob = True; - pjob.nt_devmode = nt_devmode; - - fstrcpy(pjob.jobname, jobname); - - if ((vuser = get_valid_user_struct(user->vuid)) != NULL) { - fstrcpy(pjob.user, lp_printjob_username(snum)); - standard_sub_basic(vuser->user.smb_name, vuser->user.domain, - pjob.user, sizeof(pjob.user)-1); - /* ensure NULL termination */ - pjob.user[sizeof(pjob.user)-1] = '\0'; - } else { - fstrcpy(pjob.user, uidtoname(user->ut.uid)); - } + pjob.devmode = devmode; + + fstrcpy(pjob.jobname, docname); + + fstrcpy(pjob.user, lp_printjob_username(snum)); + standard_sub_advanced(sharename, server_info->sanitized_username, + path, server_info->utok.gid, + server_info->sanitized_username, + server_info->info3->base.domain.string, + pjob.user, sizeof(pjob.user)-1); + /* ensure NULL termination */ + pjob.user[sizeof(pjob.user)-1] = '\0'; fstrcpy(pjob.queuename, lp_const_servicename(snum)); /* we have a job entry - now create the spool file */ - slprintf(pjob.filename, sizeof(pjob.filename)-1, "%s/%s%.8u.XXXXXX", - path, PRINT_SPOOL_PREFIX, (unsigned int)jobid); - pjob.fd = smb_mkstemp(pjob.filename); - - if (pjob.fd == -1) { - if (errno == EACCES) { - /* Common setup error, force a report. */ - DEBUG(0, ("print_job_start: insufficient permissions \ -to open spool file %s.\n", pjob.filename)); - } else { - /* Normal case, report at level 3 and above. */ - DEBUG(3, ("print_job_start: can't open spool file %s,\n", pjob.filename)); - DEBUGADD(3, ("errno = %d (%s).\n", errno, strerror(errno))); - } + werr = print_job_spool_file(snum, jobid, filename, &pjob); + if (!W_ERROR_IS_OK(werr)) { goto fail; } @@ -2471,16 +2708,19 @@ to open spool file %s.\n", pjob.filename)); release_print_db(pdb); - return jobid; + *_jobid = jobid; + return WERR_OK; - fail: - if (jobid != -1) +fail: + if (jobid != -1) { pjob_delete(sharename, jobid); + } release_print_db(pdb); - DEBUG(3, ("print_job_start: returning fail. Error = %s\n", strerror(errno) )); - return (uint32)-1; + DEBUG(3, ("print_job_start: returning fail. " + "Error = %s\n", win_errstr(werr))); + return werr; } /**************************************************************************** @@ -2509,36 +2749,61 @@ void print_job_endpage(int snum, uint32 jobid) error. ****************************************************************************/ -bool print_job_end(int snum, uint32 jobid, enum file_close_type close_type) +NTSTATUS print_job_end(int snum, uint32 jobid, enum file_close_type close_type) { const char* sharename = lp_const_servicename(snum); struct printjob *pjob; int ret; SMB_STRUCT_STAT sbuf; struct printif *current_printif = get_printer_fns( snum ); + NTSTATUS status = NT_STATUS_UNSUCCESSFUL; pjob = print_job_find(sharename, jobid); - if (!pjob) - return False; + if (!pjob) { + return NT_STATUS_PRINT_CANCELLED; + } - if (pjob->spooled || pjob->pid != sys_getpid()) - return False; + if (pjob->spooled || pjob->pid != sys_getpid()) { + return NT_STATUS_ACCESS_DENIED; + } + + if (close_type == NORMAL_CLOSE || close_type == SHUTDOWN_CLOSE) { + if (pjob->status == PJOB_SMBD_SPOOLING) { + /* take over the file now, smbd is done */ + if (sys_stat(pjob->filename, &sbuf, false) != 0) { + status = map_nt_error_from_unix(errno); + DEBUG(3, ("print_job_end: " + "stat file failed for jobid %d\n", + jobid)); + goto fail; + } + + pjob->status = LPQ_SPOOLING; + + } else { + + if ((sys_fstat(pjob->fd, &sbuf, false) != 0)) { + status = map_nt_error_from_unix(errno); + close(pjob->fd); + DEBUG(3, ("print_job_end: " + "stat file failed for jobid %d\n", + jobid)); + goto fail; + } + + close(pjob->fd); + } - if ((close_type == NORMAL_CLOSE || close_type == SHUTDOWN_CLOSE) && - (sys_fstat(pjob->fd, &sbuf) == 0)) { - pjob->size = sbuf.st_size; - close(pjob->fd); - pjob->fd = -1; + pjob->size = sbuf.st_ex_size; } else { - /* - * Not a normal close or we couldn't stat the job file, - * so something has gone wrong. Cleanup. + /* + * Not a normal close, something has gone wrong. Cleanup. */ - close(pjob->fd); - pjob->fd = -1; - DEBUG(3,("print_job_end: failed to stat file for jobid %d\n", jobid )); + if (pjob->fd != -1) { + close(pjob->fd); + } goto fail; } @@ -2551,35 +2816,36 @@ bool print_job_end(int snum, uint32 jobid, enum file_close_type close_type) pjob->filename, pjob->size ? "deleted" : "zero length" )); unlink(pjob->filename); pjob_delete(sharename, jobid); - return True; + return NT_STATUS_OK; } - pjob->smbjob = jobid; - ret = (*(current_printif->job_submit))(snum, pjob); - if (ret) + if (ret) { + status = NT_STATUS_PRINT_CANCELLED; goto fail; + } + + /* The print job has been successfully handed over to the back-end */ - /* The print job has been sucessfully handed over to the back-end */ - pjob->spooled = True; pjob->status = LPQ_QUEUED; pjob_store(sharename, jobid, pjob); - + /* make sure the database is up to date */ if (print_cache_expired(lp_const_servicename(snum), True)) - print_queue_update(snum, False); - - return True; + print_queue_update(server_messaging_context(), snum, False); + + return NT_STATUS_OK; fail: - /* The print job was not succesfully started. Cleanup */ + /* The print job was not successfully started. Cleanup */ /* Still need to add proper error return propagation! 010122:JRR */ + pjob->fd = -1; unlink(pjob->filename); pjob_delete(sharename, jobid); - return False; + return status; } /**************************************************************************** @@ -2601,8 +2867,8 @@ static bool get_stored_queue_info(struct tdb_print_db *pdb, int snum, int *pcoun /* make sure the database is up to date */ if (print_cache_expired(lp_const_servicename(snum), True)) - print_queue_update(snum, False); - + print_queue_update(server_messaging_context(), snum, False); + *pcount = 0; *ppqueue = NULL; @@ -2611,10 +2877,10 @@ static bool get_stored_queue_info(struct tdb_print_db *pdb, int snum, int *pcoun /* Get the stored queue data. */ data = tdb_fetch(pdb->tdb, string_tdb_data("INFO/linear_queue_array")); - + if (data.dptr && data.dsize >= sizeof(qcount)) len += tdb_unpack(data.dptr + len, data.dsize - len, "d", &qcount); - + /* Get the changed jobs list. */ cgdata = tdb_fetch(pdb->tdb, string_tdb_data("INFO/jobs_changed")); if (cgdata.dptr != NULL && (cgdata.dsize % 4 == 0)) @@ -2680,7 +2946,7 @@ static bool get_stored_queue_info(struct tdb_print_db *pdb, int snum, int *pcoun /* Sort the queue by submission time otherwise they are displayed in hash order. */ - qsort(queue, total_count, sizeof(print_queue_struct), QSORT_CAST(printjob_comp)); + TYPESAFE_QSORT(queue, total_count, printjob_comp); DEBUG(5,("get_stored_queue_info: total_count = %u\n", (unsigned int)total_count)); @@ -2704,7 +2970,7 @@ static bool get_stored_queue_info(struct tdb_print_db *pdb, int snum, int *pcoun set queue = NULL and status = NULL if you just want to update the cache ****************************************************************************/ -int print_queue_status(int snum, +int print_queue_status(int snum, print_queue_struct **ppqueue, print_status_struct *status) { @@ -2717,7 +2983,7 @@ int print_queue_status(int snum, /* make sure the database is up to date */ if (print_cache_expired(lp_const_servicename(snum), True)) - print_queue_update(snum, False); + print_queue_update(server_messaging_context(), snum, False); /* return if we are done */ if ( !ppqueue || !status ) @@ -2742,7 +3008,7 @@ int print_queue_status(int snum, data = tdb_fetch(pdb->tdb, key); if (data.dptr) { if (data.dsize == sizeof(*status)) { - /* this memcpy is ok since the status struct was + /* this memcpy is ok since the status struct was not packed before storing it in the tdb */ memcpy(status, data.dptr, sizeof(*status)); } @@ -2767,26 +3033,26 @@ int print_queue_status(int snum, Pause a queue. ****************************************************************************/ -bool print_queue_pause(struct current_user *user, int snum, WERROR *errcode) +WERROR print_queue_pause(struct auth_serversupplied_info *server_info, + struct messaging_context *msg_ctx, int snum) { int ret; struct printif *current_printif = get_printer_fns( snum ); - - if (!print_access_check(user, snum, PRINTER_ACCESS_ADMINISTER)) { - *errcode = WERR_ACCESS_DENIED; - return False; + + if (!print_access_check(server_info, msg_ctx, snum, + PRINTER_ACCESS_ADMINISTER)) { + return WERR_ACCESS_DENIED; } - + become_root(); - + ret = (*(current_printif->queue_pause))(snum); unbecome_root(); - + if (ret != 0) { - *errcode = WERR_INVALID_PARAM; - return False; + return WERR_INVALID_PARAM; } /* force update the database */ @@ -2796,50 +3062,51 @@ bool print_queue_pause(struct current_user *user, int snum, WERROR *errcode) notify_printer_status(snum, PRINTER_STATUS_PAUSED); - return True; + return WERR_OK; } /**************************************************************************** Resume a queue. ****************************************************************************/ -bool print_queue_resume(struct current_user *user, int snum, WERROR *errcode) +WERROR print_queue_resume(struct auth_serversupplied_info *server_info, + struct messaging_context *msg_ctx, int snum) { int ret; struct printif *current_printif = get_printer_fns( snum ); - if (!print_access_check(user, snum, PRINTER_ACCESS_ADMINISTER)) { - *errcode = WERR_ACCESS_DENIED; - return False; + if (!print_access_check(server_info, msg_ctx, snum, + PRINTER_ACCESS_ADMINISTER)) { + return WERR_ACCESS_DENIED; } - + become_root(); - + ret = (*(current_printif->queue_resume))(snum); unbecome_root(); - + if (ret != 0) { - *errcode = WERR_INVALID_PARAM; - return False; + return WERR_INVALID_PARAM; } /* make sure the database is up to date */ if (print_cache_expired(lp_const_servicename(snum), True)) - print_queue_update(snum, True); + print_queue_update(msg_ctx, snum, True); /* Send a printer notify message */ notify_printer_status(snum, PRINTER_STATUS_OK); - return True; + return WERR_OK; } /**************************************************************************** Purge a queue - implemented by deleting all jobs that we can delete. ****************************************************************************/ -bool print_queue_purge(struct current_user *user, int snum, WERROR *errcode) +WERROR print_queue_purge(struct auth_serversupplied_info *server_info, + struct messaging_context *msg_ctx, int snum) { print_queue_struct *queue; print_status_struct status; @@ -2847,29 +3114,33 @@ bool print_queue_purge(struct current_user *user, int snum, WERROR *errcode) bool can_job_admin; /* Force and update so the count is accurate (i.e. not a cached count) */ - print_queue_update(snum, True); - - can_job_admin = print_access_check(user, snum, JOB_ACCESS_ADMINISTER); + print_queue_update(msg_ctx, snum, True); + + can_job_admin = print_access_check(server_info, + msg_ctx, + snum, + JOB_ACCESS_ADMINISTER); njobs = print_queue_status(snum, &queue, &status); - + if ( can_job_admin ) become_root(); for (i=0;i