X-Git-Url: http://git.samba.org/samba.git/?p=jra%2Fsamba%2F.git;a=blobdiff_plain;f=source3%2Fprinting%2Fprinting.c;h=ad17213c2d65f0d0efeb83cb0d3cad1f446b03fd;hp=c0cd44d1eccb22fa24e8f8a0942092901dd9477f;hb=6ce882ef292b1e785b8b859b48256da11091c0d1;hpb=e519e528a0858a71ad7d3b821accce4a27d5b75f diff --git a/source3/printing/printing.c b/source3/printing/printing.c index c0cd44d1ecc..ad17213c2d6 100644 --- a/source3/printing/printing.c +++ b/source3/printing/printing.c @@ -20,10 +20,12 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include "includes.h" #include "printing.h" /* Current printer interface */ static struct printif *current_printif = &generic_printif; +static BOOL remove_from_jobs_changed(int snum, uint32 jobid); /* the printing backend revolves around a tdb database that stores the @@ -53,6 +55,8 @@ uint16 pjobid_to_rap(int snum, uint32 jobid) TDB_DATA data, key; char jinfo[8]; + DEBUG(10,("pjobid_to_rap: called.\n")); + if (!rap_tdb) { /* Create the in-memory tdb. */ rap_tdb = tdb_open_log(NULL, 0, TDB_INTERNAL, (O_RDWR|O_CREAT), 0644); @@ -69,8 +73,12 @@ uint16 pjobid_to_rap(int snum, uint32 jobid) if (data.dptr && data.dsize == sizeof(uint16)) { memcpy(&rap_jobid, data.dptr, sizeof(uint16)); SAFE_FREE(data.dptr); + DEBUG(10,("pjobid_to_rap: jobid %u maps to RAP jobid %u\n", + (unsigned int)jobid, + (unsigned int)rap_jobid)); return rap_jobid; } + SAFE_FREE(data.dptr); /* Not found - create and store mapping. */ rap_jobid = ++next_rap_jobid; if (rap_jobid == 0) @@ -79,13 +87,18 @@ uint16 pjobid_to_rap(int snum, uint32 jobid) data.dsize = sizeof(rap_jobid); tdb_store(rap_tdb, key, data, TDB_REPLACE); tdb_store(rap_tdb, data, key, TDB_REPLACE); + + DEBUG(10,("pjobid_to_rap: created jobid %u maps to RAP jobid %u\n", + (unsigned int)jobid, + (unsigned int)rap_jobid)); return rap_jobid; } BOOL rap_to_pjobid(uint16 rap_jobid, int *psnum, uint32 *pjobid) { TDB_DATA data, key; - char jinfo[8]; + + DEBUG(10,("rap_to_pjobid called.\n")); if (!rap_tdb) return False; @@ -93,12 +106,19 @@ BOOL rap_to_pjobid(uint16 rap_jobid, int *psnum, uint32 *pjobid) key.dptr = (char *)&rap_jobid; key.dsize = sizeof(rap_jobid); data = tdb_fetch(rap_tdb, key); - if (data.dptr && data.dsize == sizeof(jinfo)) { - *psnum = IVAL(&jinfo,0); - *pjobid = IVAL(&jinfo,4); + if (data.dptr && data.dsize == 8) { + *psnum = IVAL(data.dptr,0); + *pjobid = IVAL(data.dptr,4); + DEBUG(10,("rap_to_pjobid: jobid %u maps to RAP jobid %u\n", + (unsigned int)*pjobid, + (unsigned int)rap_jobid)); SAFE_FREE(data.dptr); return True; } + + DEBUG(10,("rap_to_pjobid: Failed to lookup RAP jobid %u\n", + (unsigned int)rap_jobid)); + SAFE_FREE(data.dptr); return False; } @@ -108,6 +128,8 @@ static void rap_jobid_delete(int snum, uint32 jobid) uint16 rap_jobid; char jinfo[8]; + DEBUG(10,("rap_jobid_delete: called.\n")); + if (!rap_tdb) return; @@ -117,8 +139,15 @@ static void rap_jobid_delete(int snum, uint32 jobid) key.dptr = (char *)&jinfo; key.dsize = sizeof(jinfo); data = tdb_fetch(rap_tdb, key); - if (!data.dptr || (data.dsize != sizeof(uint16))) + if (!data.dptr || (data.dsize != sizeof(uint16))) { + DEBUG(10,("rap_jobid_delete: cannot find jobid %u\n", + (unsigned int)jobid )); + SAFE_FREE(data.dptr); return; + } + + DEBUG(10,("rap_jobid_delete: deleting jobid %u\n", + (unsigned int)jobid )); memcpy(&rap_jobid, data.dptr, sizeof(uint16)); SAFE_FREE(data.dptr); @@ -132,124 +161,13 @@ static pid_t local_pid; static int get_queue_status(int, print_status_struct *); -/* There can be this many printing tdb's open, plus any locked ones. */ -#define MAX_PRINT_DBS_OPEN 1 - -struct tdb_print_db { - struct tdb_print_db *next, *prev; - TDB_CONTEXT *tdb; - int locked; - fstring printer_name; -}; - -static struct tdb_print_db *print_db_head; - -/**************************************************************************** - Function to find or create the printer specific job tdb given a printername. - Limits the number of tdb's open to MAX_PRINT_DBS_OPEN. -****************************************************************************/ - -static struct tdb_print_db *get_print_db_byname(const char *printername) -{ - struct tdb_print_db *p = NULL, *last_entry = NULL; - int num_open = 0; - pstring printdb_path; - - for (p = print_db_head, last_entry = print_db_head; p; p = p->next) { - if (p->tdb && strequal(p->printer_name, printername)) { - DLIST_PROMOTE(print_db_head, p); - return p; - } - num_open++; - last_entry = p; - } - - /* Not found. */ - if (num_open >= MAX_PRINT_DBS_OPEN) { - /* Try and recycle the last entry. */ - DLIST_PROMOTE(print_db_head, last_entry); - - for (p = print_db_head; p; p = p->next) { - if (p->locked) - continue; - if (p->tdb) { - if (tdb_close(print_db_head->tdb)) { - DEBUG(0,("get_print_db: Failed to close tdb for printer %s\n", - print_db_head->printer_name )); - return NULL; - } - } - ZERO_STRUCTP(p); - break; - } - if (p) { - DLIST_PROMOTE(print_db_head, p); - p = print_db_head; - } - } - - if (!p) { - /* Create one. */ - p = (struct tdb_print_db *)malloc(sizeof(struct tdb_print_db)); - if (!p) { - DEBUG(0,("get_print_db: malloc fail !\n")); - return NULL; - } - ZERO_STRUCTP(p); - DLIST_ADD(print_db_head, p); - } - - pstrcpy(printdb_path, lock_path("printing/")); - pstrcat(printdb_path, printername); - pstrcat(printdb_path, ".tdb"); - - become_root(); - p->tdb = tdb_open_log(printdb_path, 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600); - unbecome_root(); - - if (!p->tdb) { - DEBUG(0,("get_print_db: Failed to open printer backend database %s.\n", - printdb_path )); - DLIST_REMOVE(print_db_head, p); - SAFE_FREE(p); - return NULL; - } - fstrcpy(p->printer_name, printername); - return p; -} - -/**************************************************************************** - Lock a pdb entry by string. -****************************************************************************/ - -static int pdb_entry_lock(struct tdb_print_db *pdb, char *key) -{ - if (pdb->locked == 0) { - if (tdb_lock_bystring(pdb->tdb, key) == -1) - return -1; - } - pdb->locked++; -} - /**************************************************************************** - Unlock a pdb entry by string. -****************************************************************************/ - -static void pdb_entry_unlock(struct tdb_print_db *pdb, char *key) -{ - pdb->locked--; - if (pdb->locked == 0) - tdb_unlock_bystring(pdb->tdb, key); -} - -/**************************************************************************** - Initialise the printing backend. Called once at startup. - Does not survive a fork + Initialise the printing backend. Called once at startup before the fork(). ****************************************************************************/ BOOL print_backend_init(void) { - char *sversion = "INFO/version"; + const char *sversion = "INFO/version"; pstring printing_path; int services = lp_numservices(); int snum; @@ -273,17 +191,21 @@ BOOL print_backend_init(void) pdb = get_print_db_byname(lp_const_servicename(snum)); if (!pdb) continue; - if (pdb_entry_lock(pdb, sversion) == -1) { + if (tdb_lock_bystring(pdb->tdb, sversion, 0) == -1) { DEBUG(0,("print_backend_init: Failed to open printer %s database\n", lp_const_servicename(snum) )); + release_print_db(pdb); return False; } if (tdb_fetch_int32(pdb->tdb, sversion) != PRINT_DATABASE_VERSION) { tdb_traverse(pdb->tdb, tdb_traverse_delete_fn, NULL); tdb_store_int32(pdb->tdb, sversion, PRINT_DATABASE_VERSION); } - pdb_entry_unlock(pdb, sversion); + tdb_unlock_bystring(pdb->tdb, sversion); + release_print_db(pdb); } + close_all_print_db(); /* Don't leave any open. */ + /* select the appropriate printing interface... */ #ifdef HAVE_CUPS if (strcmp(lp_printcapname(), "cups") == 0) @@ -300,16 +222,7 @@ BOOL print_backend_init(void) void printing_end(void) { - struct tdb_print_db *p; - - for (p = print_db_head; p; ) { - struct tdb_print_db *next_p = p->next; - if (p->tdb) - tdb_close(p->tdb); - DLIST_REMOVE(print_db_head, p); - SAFE_FREE(p); - p = next_p; - } + close_all_print_db(); /* Don't leave any open. */ } /**************************************************************************** @@ -327,25 +240,88 @@ static TDB_DATA print_key(uint32 jobid) return ret; } +/*********************************************************************** + unpack a pjob from a tdb buffer +***********************************************************************/ + +int unpack_pjob( char* buf, int buflen, struct printjob *pjob ) +{ + int len = 0; + int used; + uint32 pjpid, pjsysjob, pjfd, pjstarttime, pjstatus; + uint32 pjsize, pjpage_count, pjspooled, pjsmbjob; + + if ( !buf || !pjob ) + return -1; + + len += tdb_unpack(buf+len, buflen-len, "dddddddddffff", + &pjpid, + &pjsysjob, + &pjfd, + &pjstarttime, + &pjstatus, + &pjsize, + &pjpage_count, + &pjspooled, + &pjsmbjob, + pjob->filename, + pjob->jobname, + pjob->user, + pjob->queuename); + + if ( len == -1 ) + return -1; + + if ( (used = unpack_devicemode(&pjob->nt_devmode, buf+len, buflen-len)) == -1 ) + return -1; + + len += used; + + pjob->pid = pjpid; + pjob->sysjob = pjsysjob; + pjob->fd = pjfd; + pjob->starttime = pjstarttime; + pjob->status = pjstatus; + pjob->size = pjsize; + pjob->page_count = pjpage_count; + pjob->spooled = pjspooled; + pjob->smbjob = pjsmbjob; + + return len; + +} + /**************************************************************************** Useful function to find a print job in the database. ****************************************************************************/ static struct printjob *print_job_find(int snum, uint32 jobid) { - static struct printjob pjob; - TDB_DATA ret; - struct tdb_print_db *pdb = get_print_db_byname(lp_const_servicename(snum)); + static struct printjob pjob; + TDB_DATA ret; + struct tdb_print_db *pdb = get_print_db_byname(lp_const_servicename(snum)); + if (!pdb) return NULL; ret = tdb_fetch(pdb->tdb, print_key(jobid)); - if (!ret.dptr || ret.dsize != sizeof(pjob)) - return NULL; + release_print_db(pdb); - memcpy(&pjob, ret.dptr, sizeof(pjob)); - SAFE_FREE(ret.dptr); + if (!ret.dptr) + return NULL; + + if ( pjob.nt_devmode ) + free_nt_devicemode( &pjob.nt_devmode ); + + ZERO_STRUCT( pjob ); + + if ( unpack_pjob( ret.dptr, ret.dsize, &pjob ) == -1 ) { + SAFE_FREE(ret.dptr); + return NULL; + } + + SAFE_FREE(ret.dptr); return &pjob; } @@ -356,9 +332,13 @@ static uint32 sysjob_to_jobid_value; static int unixjob_traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA data, void *state) { - struct printjob *pjob = (struct printjob *)data.dptr; + struct printjob *pjob; int *sysjob = (int *)state; + if (!data.dptr || data.dsize == 0) + return 0; + + pjob = (struct printjob *)data.dptr; if (key.dsize != sizeof(uint32)) return 0; @@ -391,6 +371,7 @@ uint32 sysjob_to_jobid(int unix_jobid) pdb = get_print_db_byname(lp_const_servicename(snum)); if (pdb) tdb_traverse(pdb->tdb, unixjob_traverse_fn, &unix_jobid); + release_print_db(pdb); if (sysjob_to_jobid_value != (uint32)-1) return sysjob_to_jobid_value; } @@ -475,9 +456,12 @@ static void pjob_store_notify(int snum, uint32 jobid, struct printjob *old_data, static BOOL pjob_store(int snum, uint32 jobid, struct printjob *pjob) { - TDB_DATA old_data, new_data; - BOOL ret; - struct tdb_print_db *pdb = get_print_db_byname(lp_const_servicename(snum)); + TDB_DATA old_data, new_data; + BOOL ret = False; + struct tdb_print_db *pdb = get_print_db_byname(lp_const_servicename(snum)); + char *buf = NULL; + int len, newlen, buflen; + if (!pdb) return False; @@ -486,20 +470,61 @@ static BOOL pjob_store(int snum, uint32 jobid, struct printjob *pjob) old_data = tdb_fetch(pdb->tdb, print_key(jobid)); + /* Doh! Now we have to pack/unpack data since the NT_DEVICEMODE was added */ + + newlen = 0; + + do { + len = 0; + buflen = newlen; + len += tdb_pack(buf+len, buflen-len, "dddddddddffff", + (uint32)pjob->pid, + (uint32)pjob->sysjob, + (uint32)pjob->fd, + (uint32)pjob->starttime, + (uint32)pjob->status, + (uint32)pjob->size, + (uint32)pjob->page_count, + (uint32)pjob->spooled, + (uint32)pjob->smbjob, + pjob->filename, + pjob->jobname, + pjob->user, + pjob->queuename); + + len += pack_devicemode(pjob->nt_devmode, buf+len, buflen-len); + + if (buflen != len) { + char *tb; + + tb = (char *)Realloc(buf, len); + if (!tb) { + DEBUG(0,("pjob_store: failed to enlarge buffer!\n")); + goto done; + } + else + buf = tb; + newlen = len; + } + } while ( buflen != len ); + + /* Store new data */ - new_data.dptr = (void *)pjob; - new_data.dsize = sizeof(*pjob); + new_data.dptr = buf; + new_data.dsize = len; ret = (tdb_store(pdb->tdb, print_key(jobid), new_data, TDB_REPLACE) == 0); + release_print_db(pdb); + /* Send notify updates for what has changed */ - if (ret && (old_data.dsize == 0 || old_data.dsize == sizeof(*pjob))) { - pjob_store_notify( - snum, jobid, (struct printjob *)old_data.dptr, - (struct printjob *)new_data.dptr); - free(old_data.dptr); - } + if ( ret && (old_data.dsize == 0 || old_data.dsize == sizeof(*pjob)) ) + pjob_store_notify( snum, jobid, (struct printjob *)old_data.dptr, pjob ); + +done: + SAFE_FREE( old_data.dptr ); + SAFE_FREE( buf ); return ret; } @@ -508,7 +533,7 @@ static BOOL pjob_store(int snum, uint32 jobid, struct printjob *pjob) Remove a job structure from the database. ****************************************************************************/ -static void pjob_delete(int snum, uint32 jobid) +void pjob_delete(int snum, uint32 jobid) { struct printjob *pjob = print_job_find(snum, jobid); uint32 job_status = 0; @@ -520,6 +545,7 @@ static void pjob_delete(int snum, uint32 jobid) if (!pjob) { DEBUG(5, ("pjob_delete(): we were asked to delete nonexistent job %u\n", (unsigned int)jobid)); + release_print_db(pdb); return; } @@ -540,6 +566,7 @@ static void pjob_delete(int snum, uint32 jobid) /* Remove from printing.tdb */ tdb_delete(pdb->tdb, print_key(jobid)); + release_print_db(pdb); rap_jobid_delete(snum, jobid); } @@ -566,11 +593,13 @@ static uint32 print_parse_jobid(char *fname) List a unix job in the print database. ****************************************************************************/ -static void print_unix_job(int snum, print_queue_struct *q) +static void print_unix_job(int snum, print_queue_struct *q, uint32 jobid) { - uint32 jobid = q->job + UNIX_JOB_START; struct printjob pj, *old_pj; + if (jobid == (uint32)-1) + jobid = q->job + UNIX_JOB_START; + /* Preserve the timestamp on an existing unix print job */ old_pj = print_job_find(snum, jobid); @@ -584,11 +613,14 @@ static void print_unix_job(int snum, print_queue_struct *q) pj.status = q->status; pj.size = q->size; pj.spooled = True; - pj.smbjob = False; - fstrcpy(pj.filename, ""); - fstrcpy(pj.jobname, q->fs_file); - fstrcpy(pj.user, q->fs_user); - fstrcpy(pj.queuename, lp_const_servicename(snum)); + pj.smbjob = (old_pj != NULL ? True : False); + fstrcpy(pj.filename, old_pj ? old_pj->filename : ""); + if (jobid < UNIX_JOB_START) + fstrcpy(pj.jobname, old_pj ? old_pj->jobname : "Remote Downlevel Document"); + else + fstrcpy(pj.jobname, old_pj ? old_pj->jobname : q->fs_file); + fstrcpy(pj.user, old_pj ? old_pj->user : q->fs_user); + fstrcpy(pj.queuename, old_pj ? old_pj->queuename : lp_const_servicename(snum)); pjob_store(snum, jobid, &pj); } @@ -597,6 +629,7 @@ static void print_unix_job(int snum, print_queue_struct *q) struct traverse_struct { print_queue_struct *queue; int qcount, snum, maxcount, total_jobs; + time_t lpq_time; }; /**************************************************************************** @@ -610,10 +643,14 @@ static int traverse_fn_delete(TDB_CONTEXT *t, TDB_DATA key, TDB_DATA data, void uint32 jobid; int i; - if (data.dsize != sizeof(pjob) || key.dsize != sizeof(jobid)) + if ( key.dsize != sizeof(jobid) ) return 0; + memcpy(&jobid, key.dptr, sizeof(jobid)); - memcpy(&pjob, data.dptr, sizeof(pjob)); + if ( unpack_pjob( data.dptr, data.dsize, &pjob ) == -1 ) + return 0; + free_nt_devicemode( &pjob.nt_devmode ); + if (ts->snum != lp_servicenumber(pjob.queuename)) { /* this isn't for the queue we are looking at - this cannot happen with the split tdb's. JRA */ @@ -628,9 +665,11 @@ static int traverse_fn_delete(TDB_CONTEXT *t, TDB_DATA key, TDB_DATA data, void if (jobid == u_jobid) break; } - if (i == ts->qcount) + if (i == ts->qcount) { + DEBUG(10,("traverse_fn_delete: pjob %u deleted due to !smbjob\n", + (unsigned int)jobid )); pjob_delete(ts->snum, jobid); - else + } else ts->total_jobs++; return 0; } @@ -640,9 +679,11 @@ static int traverse_fn_delete(TDB_CONTEXT *t, TDB_DATA key, TDB_DATA data, void /* if a job is not spooled and the process doesn't exist then kill it. This cleans up after smbd deaths */ - if (!process_exists(pjob.pid)) + if (!process_exists(pjob.pid)) { + DEBUG(10,("traverse_fn_delete: pjob %u deleted due to !process_exists (%u)\n", + (unsigned int)jobid, (unsigned int)pjob.pid )); pjob_delete(ts->snum, jobid); - else + } else ts->total_jobs++; return 0; } @@ -657,23 +698,36 @@ static int traverse_fn_delete(TDB_CONTEXT *t, TDB_DATA key, TDB_DATA data, void completed, so delete the database entry. */ if (i == ts->qcount) { - time_t cur_t = time(NULL); /* A race can occur between the time a job is spooled and when it appears in the lpq output. This happens when the job is added to printing.tdb when another smbd running print_queue_update() has completed a lpq and is currently traversing the printing tdb and deleting jobs. - A workaround is to not delete the job if it has been - submitted less than lp_lpqcachetime() seconds ago. */ + Don't delete the job if it was submitted after the lpq_time. */ - if ((cur_t - pjob.starttime) > lp_lpqcachetime()) + if (pjob.starttime < ts->lpq_time) { + DEBUG(10,("traverse_fn_delete: pjob %u deleted due to pjob.starttime (%u) < ts->lpq_time (%u)\n", + (unsigned int)jobid, + (unsigned int)pjob.starttime, + (unsigned int)ts->lpq_time )); pjob_delete(ts->snum, jobid); - else + } else ts->total_jobs++; + return 0; } - else - ts->total_jobs++; + + /* Save the pjob attributes we will store. */ + 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; + ts->queue[i].priority = 1; + ts->queue[i].time = pjob.starttime; + fstrcpy(ts->queue[i].fs_user, pjob.user); + fstrcpy(ts->queue[i].fs_file, pjob.jobname); + + ts->total_jobs++; return 0; } @@ -692,6 +746,7 @@ static void print_cache_flush(int snum) return; slprintf(key, sizeof(key)-1, "CACHE/%s", printername); tdb_store_int32(pdb->tdb, key, -1); + release_print_db(pdb); } /**************************************************************************** @@ -712,8 +767,11 @@ static pid_t get_updating_pid(fstring printer_name) key.dsize = strlen(keystr); data = tdb_fetch(pdb->tdb, key); - if (!data.dptr || data.dsize != sizeof(pid_t)) + release_print_db(pdb); + if (!data.dptr || data.dsize != sizeof(pid_t)) { + SAFE_FREE(data.dptr); return (pid_t)-1; + } memcpy(&updating_pid, data.dptr, sizeof(pid_t)); SAFE_FREE(data.dptr); @@ -746,6 +804,7 @@ static void set_updating_pid(const fstring printer_name, BOOL delete) if (delete) { tdb_delete(pdb->tdb, key); + release_print_db(pdb); return; } @@ -753,6 +812,117 @@ static void set_updating_pid(const fstring printer_name, BOOL delete) data.dsize = sizeof(pid_t); tdb_store(pdb->tdb, key, data, TDB_REPLACE); + release_print_db(pdb); +} + +/**************************************************************************** + Sort print jobs by submittal time. +****************************************************************************/ + +static int printjob_comp(print_queue_struct *j1, print_queue_struct *j2) +{ + /* Silly cases */ + + if (!j1 && !j2) + return 0; + if (!j1) + return -1; + if (!j2) + return 1; + + /* Sort on job start time */ + + if (j1->time == j2->time) + return 0; + return (j1->time > j2->time) ? 1 : -1; +} + +/**************************************************************************** + Store the sorted queue representation for later portmon retrieval. +****************************************************************************/ + +static void store_queue_struct(struct tdb_print_db *pdb, struct traverse_struct *pts) +{ + TDB_DATA data, key; + int max_reported_jobs = lp_max_reported_jobs(pts->snum); + print_queue_struct *queue = pts->queue; + size_t len; + size_t i; + uint qcount; + + if (max_reported_jobs && (max_reported_jobs < pts->qcount)) + pts->qcount = max_reported_jobs; + qcount = pts->qcount; + + /* Work out the size. */ + data.dsize = 0; + data.dsize += tdb_pack(NULL, 0, "d", qcount); + + for (i = 0; i < pts->qcount; i++) { + data.dsize += tdb_pack(NULL, 0, "ddddddff", + (uint32)queue[i].job, + (uint32)queue[i].size, + (uint32)queue[i].page_count, + (uint32)queue[i].status, + (uint32)queue[i].priority, + (uint32)queue[i].time, + queue[i].fs_user, + queue[i].fs_file); + } + + if ((data.dptr = malloc(data.dsize)) == NULL) + return; + + len = 0; + len += tdb_pack(data.dptr + len, data.dsize - len, "d", qcount); + for (i = 0; i < pts->qcount; i++) { + len += tdb_pack(data.dptr + len, data.dsize - len, "ddddddff", + (uint32)queue[i].job, + (uint32)queue[i].size, + (uint32)queue[i].page_count, + (uint32)queue[i].status, + (uint32)queue[i].priority, + (uint32)queue[i].time, + queue[i].fs_user, + queue[i].fs_file); + } + + key.dptr = "INFO/linear_queue_array"; + key.dsize = strlen(key.dptr); + tdb_store(pdb->tdb, key, data, TDB_REPLACE); + SAFE_FREE(data.dptr); + return; +} + +static TDB_DATA get_jobs_changed_data(struct tdb_print_db *pdb) +{ + TDB_DATA data, key; + + key.dptr = "INFO/jobs_changed"; + key.dsize = strlen(key.dptr); + ZERO_STRUCT(data); + + data = tdb_fetch(pdb->tdb, key); + if (data.dptr == NULL || data.dsize == 0 || (data.dsize % 4 != 0)) { + SAFE_FREE(data.dptr); + ZERO_STRUCT(data); + } + + return data; +} + +static void check_job_changed(int snum, TDB_DATA data, uint32 jobid) +{ + unsigned int i; + unsigned int job_count = data.dsize / 4; + + for (i = 0; i < job_count; i++) { + uint32 ch_jobid; + + memcpy(&ch_jobid, data.dptr + (i*4), 4); + if (ch_jobid == jobid) + remove_from_jobs_changed(snum, jobid); + } } /**************************************************************************** @@ -769,6 +939,7 @@ static void print_queue_update(int snum) struct traverse_struct tstruct; fstring keystr, printer_name, cachestr; TDB_DATA data, key; + TDB_DATA jcdata; struct tdb_print_db *pdb; fstrcpy(printer_name, lp_const_servicename(snum)); @@ -781,14 +952,18 @@ static void print_queue_update(int snum) * This is essentially a mutex on the update. */ - if (get_updating_pid(printer_name) != -1) + if (get_updating_pid(printer_name) != -1) { + release_print_db(pdb); return; + } /* Lock the queue for the database update */ slprintf(keystr, sizeof(keystr) - 1, "LOCK/%s", printer_name); - if (pdb_entry_lock(pdb, keystr) == -1) { + /* Only wait 10 seconds for this. */ + if (tdb_lock_bystring(pdb->tdb, keystr, 10) == -1) { DEBUG(0,("print_queue_update: Failed to lock printer %s database\n", printer_name)); + release_print_db(pdb); return; } @@ -802,7 +977,8 @@ static void print_queue_update(int snum) /* * Someone else is doing the update, exit. */ - pdb_entry_unlock(pdb, keystr); + tdb_unlock_bystring(pdb->tdb, keystr); + release_print_db(pdb); return; } @@ -818,7 +994,7 @@ static void print_queue_update(int snum) * the update. */ - pdb_entry_unlock(pdb, keystr); + tdb_unlock_bystring(pdb->tdb, keystr); /* * Update the cache time FIRST ! Stops others even @@ -837,6 +1013,12 @@ static void print_queue_update(int snum) DEBUG(3, ("%d job%s in queue for %s\n", qcount, (qcount != 1) ? "s" : "", printer_name)); + /* Sort the queue by submission time otherwise they are displayed + in hash order. */ + + qsort(queue, qcount, sizeof(print_queue_struct), + QSORT_CAST(printjob_comp)); + /* any job in the internal database that is marked as spooled and doesn't exist in the system queue is considered finished @@ -847,12 +1029,15 @@ static void print_queue_update(int snum) fill in any system job numbers as we go */ + + jcdata = get_jobs_changed_data(pdb); + for (i=0; isysjob = queue[i].job; pjob->status = queue[i].status; - pjob_store(snum, jobid, pjob); + check_job_changed(snum, jcdata, jobid); } + SAFE_FREE(jcdata.dptr); + /* now delete any queued entries that don't appear in the system queue */ tstruct.queue = queue; tstruct.qcount = qcount; tstruct.snum = snum; tstruct.total_jobs = 0; + tstruct.lpq_time = time(NULL); tdb_traverse(pdb->tdb, traverse_fn_delete, (void *)&tstruct); + /* Store the linearised queue, max jobs only. */ + store_queue_struct(pdb, &tstruct); + SAFE_FREE(tstruct.queue); + DEBUG(10,("print_queue_update: printer %s INFO/total_jobs = %d\n", + printer_name, tstruct.total_jobs )); + tdb_store_int32(pdb->tdb, "INFO/total_jobs", tstruct.total_jobs); - if( qcount != get_queue_status(snum, &old_status)) + get_queue_status(snum, &old_status); + if (old_status.qcount != qcount) DEBUG(10,("print_queue_update: queue status change %d jobs -> %d jobs for printer %s\n", old_status.qcount, qcount, printer_name )); @@ -909,6 +1104,188 @@ static void print_queue_update(int snum) /* Delete our pid from the db. */ set_updating_pid(printer_name, True); + release_print_db(pdb); +} + +/**************************************************************************** + Create/Update an entry in the print tdb that will allow us to send notify + updates only to interested smbd's. +****************************************************************************/ + +BOOL print_notify_register_pid(int snum) +{ + TDB_DATA data; + struct tdb_print_db *pdb = NULL; + TDB_CONTEXT *tdb = NULL; + const char *printername; + uint32 mypid = (uint32)sys_getpid(); + BOOL ret = False; + size_t i; + + /* 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) + { + int num_services = lp_numservices(); + int idx; + + for ( idx=0; idxtdb; + } + + if (tdb_lock_bystring(tdb, NOTIFY_PID_LIST_KEY, 10) == -1) { + DEBUG(0,("print_notify_register_pid: Failed to lock printer %s\n", + printername)); + if (pdb) + release_print_db(pdb); + return False; + } + + data = get_printer_notify_pid_list( tdb, printername, True ); + + /* Add ourselves and increase the refcount. */ + + for (i = 0; i < data.dsize; i += 8) { + if (IVAL(data.dptr,i) == mypid) { + uint32 new_refcount = IVAL(data.dptr, i+4) + 1; + SIVAL(data.dptr, i+4, new_refcount); + break; + } + } + + if (i == data.dsize) { + /* We weren't in the list. Realloc. */ + data.dptr = Realloc(data.dptr, data.dsize + 8); + if (!data.dptr) { + DEBUG(0,("print_notify_register_pid: Relloc fail for printer %s\n", + printername)); + goto done; + } + data.dsize += 8; + SIVAL(data.dptr,data.dsize - 8,mypid); + SIVAL(data.dptr,data.dsize - 4,1); /* Refcount. */ + } + + /* Store back the record. */ + if (tdb_store_bystring(tdb, NOTIFY_PID_LIST_KEY, data, TDB_REPLACE) == -1) { + DEBUG(0,("print_notify_register_pid: Failed to update pid \ +list for printer %s\n", printername)); + goto done; + } + + ret = True; + + done: + + tdb_unlock_bystring(tdb, NOTIFY_PID_LIST_KEY); + if (pdb) + release_print_db(pdb); + SAFE_FREE(data.dptr); + return ret; +} + +/**************************************************************************** + Update an entry in the print tdb that will allow us to send notify + updates only to interested smbd's. +****************************************************************************/ + +BOOL print_notify_deregister_pid(int snum) +{ + TDB_DATA data; + struct tdb_print_db *pdb = NULL; + TDB_CONTEXT *tdb = NULL; + const char *printername; + uint32 mypid = (uint32)sys_getpid(); + size_t i; + BOOL ret = False; + + /* if ( snum == -1 ), we are deregister a print server handle + which means to deregister on all print queues */ + + if (snum == -1) + { + int num_services = lp_numservices(); + int idx; + + for ( idx=0; idxtdb; + } + + if (tdb_lock_bystring(tdb, NOTIFY_PID_LIST_KEY, 10) == -1) { + DEBUG(0,("print_notify_register_pid: Failed to lock \ +printer %s database\n", printername)); + if (pdb) + release_print_db(pdb); + return False; + } + + data = get_printer_notify_pid_list( tdb, printername, True ); + + /* Reduce refcount. Remove ourselves if zero. */ + + for (i = 0; i < data.dsize; ) { + if (IVAL(data.dptr,i) == mypid) { + uint32 refcount = IVAL(data.dptr, i+4); + + refcount--; + + if (refcount == 0) { + if (data.dsize - i > 8) + memmove( &data.dptr[i], &data.dptr[i+8], data.dsize - i - 8); + data.dsize -= 8; + continue; + } + SIVAL(data.dptr, i+4, refcount); + } + + i += 8; + } + + if (data.dsize == 0) + SAFE_FREE(data.dptr); + + /* Store back the record. */ + if (tdb_store_bystring(tdb, NOTIFY_PID_LIST_KEY, data, TDB_REPLACE) == -1) { + DEBUG(0,("print_notify_register_pid: Failed to update pid \ +list for printer %s\n", printername)); + goto done; + } + + ret = True; + + done: + + tdb_unlock_bystring(tdb, NOTIFY_PID_LIST_KEY); + if (pdb) + release_print_db(pdb); + SAFE_FREE(data.dptr); + return ret; } /**************************************************************************** @@ -918,9 +1295,13 @@ static void print_queue_update(int snum) BOOL print_job_exists(int snum, uint32 jobid) { struct tdb_print_db *pdb = get_print_db_byname(lp_const_servicename(snum)); + BOOL ret; + if (!pdb) return False; - return tdb_exists(pdb->tdb, print_key(jobid)); + ret = tdb_exists(pdb->tdb, print_key(jobid)); + release_print_db(pdb); + return ret; } /**************************************************************************** @@ -952,6 +1333,23 @@ char *print_job_fname(int snum, uint32 jobid) return pjob->filename; } + +/**************************************************************************** + Give the filename used for a jobid. + Only valid for the process doing the spooling and when the job + has not been spooled. +****************************************************************************/ + +NT_DEVICEMODE *print_job_devmode(int snum, uint32 jobid) +{ + struct printjob *pjob = print_job_find(snum, jobid); + + if ( !pjob ) + return NULL; + + return pjob->nt_devmode; +} + /**************************************************************************** Set the place in the queue for a job. ****************************************************************************/ @@ -976,6 +1374,62 @@ BOOL print_job_set_name(int snum, uint32 jobid, char *name) return pjob_store(snum, jobid, pjob); } +/*************************************************************************** + Remove a jobid from the 'jobs changed' list. +***************************************************************************/ + +static BOOL remove_from_jobs_changed(int snum, uint32 jobid) +{ + const char *printername = lp_const_servicename(snum); + struct tdb_print_db *pdb = get_print_db_byname(printername); + TDB_DATA data, key; + size_t job_count, i; + BOOL ret = False; + BOOL gotlock = False; + + key.dptr = "INFO/jobs_changed"; + key.dsize = strlen(key.dptr); + ZERO_STRUCT(data); + + if (tdb_chainlock_with_timeout(pdb->tdb, key, 5) == -1) + goto out; + + gotlock = True; + + data = tdb_fetch(pdb->tdb, key); + + if (data.dptr == NULL || data.dsize == 0 || (data.dsize % 4 != 0)) + goto out; + + job_count = data.dsize / 4; + for (i = 0; i < job_count; i++) { + uint32 ch_jobid; + + memcpy(&ch_jobid, data.dptr + (i*4), 4); + if (ch_jobid == jobid) { + if (i < job_count -1 ) + memmove(data.dptr + (i*4), data.dptr + (i*4) + 4, (job_count - i - 1)*4 ); + data.dsize -= 4; + if (tdb_store(pdb->tdb, key, data, TDB_REPLACE) == -1) + goto out; + break; + } + } + + ret = True; + out: + + if (gotlock) + tdb_chainunlock(pdb->tdb, key); + SAFE_FREE(data.dptr); + release_print_db(pdb); + if (ret) + DEBUG(10,("remove_from_jobs_changed: removed jobid %u\n", (unsigned int)jobid )); + else + DEBUG(10,("remove_from_jobs_changed: Failed to remove jobid %u\n", (unsigned int)jobid )); + return ret; +} + /**************************************************************************** Delete a print job - don't update queue. ****************************************************************************/ @@ -1009,12 +1463,24 @@ static BOOL print_job_delete1(int snum, uint32 jobid) if (pjob->spooled && pjob->sysjob != -1) result = (*(current_printif->job_delete))(snum, pjob); + else + remove_from_jobs_changed(snum, jobid); - /* Delete the tdb entry if the delete suceeded or the job hasn't + /* Delete the tdb entry if the delete succeeded or the job hasn't been spooled. */ - if (result == 0) + if (result == 0) { + const char *printername = lp_const_servicename(snum); + struct tdb_print_db *pdb = get_print_db_byname(printername); + int njobs = 1; + + if (!pdb) + return False; pjob_delete(snum, jobid); + /* Ensure we keep a rough count of the number of total jobs... */ + tdb_change_int32_atomic(pdb->tdb, "INFO/total_jobs", &njobs, -1); + release_print_db(pdb); + } return (result == 0); } @@ -1044,8 +1510,11 @@ static BOOL is_owner(struct current_user *user, int snum, uint32 jobid) BOOL print_job_delete(struct current_user *user, int snum, uint32 jobid, WERROR *errcode) { - BOOL owner; + BOOL owner, deleted; + char *fname; + *errcode = WERR_OK; + owner = is_owner(user, snum, jobid); /* Check access against security descriptor or whether the user @@ -1055,18 +1524,51 @@ BOOL print_job_delete(struct current_user *user, int snum, uint32 jobid, WERROR !print_access_check(user, snum, JOB_ACCESS_ADMINISTER)) { DEBUG(3, ("delete denied by security descriptor\n")); *errcode = WERR_ACCESS_DENIED; + + /* BEGIN_ADMIN_LOG */ + sys_adminlog( LOG_ERR, + "Permission denied-- user not allowed to delete, \ +pause, or resume print job. User name: %s. Printer name: %s.", + uidtoname(user->uid), PRINTERNAME(snum) ); + /* END_ADMIN_LOG */ + return False; } - if (!print_job_delete1(snum, jobid)) + /* + * 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 + * spool file & return. + */ + + if ( (fname = print_job_fname( snum, jobid )) != 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; + } + + return True; + } + + if (!print_job_delete1(snum, jobid)) { + *errcode = WERR_ACCESS_DENIED; return False; + } /* force update the database and say the delete failed if the job still exists */ print_queue_update(snum); + + deleted = !print_job_exists(snum, jobid); + if ( !deleted ) + *errcode = WERR_ACCESS_DENIED; - return !print_job_exists(snum, jobid); + return deleted; } /**************************************************************************** @@ -1087,6 +1589,14 @@ BOOL print_job_pause(struct current_user *user, int snum, uint32 jobid, WERROR * if (!is_owner(user, snum, jobid) && !print_access_check(user, snum, JOB_ACCESS_ADMINISTER)) { DEBUG(3, ("pause denied by security descriptor\n")); + + /* BEGIN_ADMIN_LOG */ + sys_adminlog( LOG_ERR, + "Permission denied-- user not allowed to delete, \ +pause, or resume print job. User name: %s. Printer name: %s.", + uidtoname(user->uid), PRINTERNAME(snum) ); + /* END_ADMIN_LOG */ + *errcode = WERR_ACCESS_DENIED; return False; } @@ -1130,6 +1640,13 @@ BOOL print_job_resume(struct current_user *user, int snum, uint32 jobid, WERROR !print_access_check(user, snum, JOB_ACCESS_ADMINISTER)) { DEBUG(3, ("resume denied by security descriptor\n")); *errcode = WERR_ACCESS_DENIED; + + /* BEGIN_ADMIN_LOG */ + sys_adminlog( LOG_ERR, + "Permission denied-- user not allowed to delete, \ +pause, or resume print job. User name: %s. Printer name: %s.", + uidtoname(user->uid), PRINTERNAME(snum) ); + /* END_ADMIN_LOG */ return False; } @@ -1205,8 +1722,10 @@ static BOOL print_cache_expired(int snum) DEBUG(3, ("print cache expired for queue %s \ (last_qscan_time = %d, time now = %d, qcachetime = %d)\n", printername, (int)last_qscan_time, (int)time_now, (int)lp_lpqcachetime() )); + release_print_db(pdb); return True; } + release_print_db(pdb); return False; } @@ -1220,21 +1739,26 @@ static int get_queue_status(int snum, print_status_struct *status) TDB_DATA data, key; const char *printername = lp_const_servicename(snum); struct tdb_print_db *pdb = get_print_db_byname(printername); + int len; + if (!pdb) return 0; - ZERO_STRUCTP(status); - slprintf(keystr, sizeof(keystr)-1, "STATUS/%s", printername); - key.dptr = keystr; - key.dsize = strlen(keystr); - data = tdb_fetch(pdb->tdb, key); - if (data.dptr) { - if (data.dsize == sizeof(print_status_struct)) { - memcpy(status, data.dptr, sizeof(print_status_struct)); + if (status) { + ZERO_STRUCTP(status); + slprintf(keystr, sizeof(keystr)-1, "STATUS/%s", printername); + key.dptr = keystr; + key.dsize = strlen(keystr); + data = tdb_fetch(pdb->tdb, key); + if (data.dptr) { + if (data.dsize == sizeof(print_status_struct)) + memcpy(status, data.dptr, sizeof(print_status_struct)); + SAFE_FREE(data.dptr); } - SAFE_FREE(data.dptr); } - return status->qcount; + len = tdb_fetch_int32(pdb->tdb, "INFO/total_jobs"); + release_print_db(pdb); + return (len == -1 ? 0 : len); } /**************************************************************************** @@ -1253,58 +1777,110 @@ int print_queue_length(int snum, print_status_struct *pstatus) /* also fetch the queue status */ memset(&status, 0, sizeof(status)); len = get_queue_status(snum, &status); + if (pstatus) *pstatus = status; + return len; } -/**************************************************************************** - Determine the number of jobs in all queues. This is very expensive. Don't - call ! JRA. -****************************************************************************/ +/*************************************************************************** + Allocate a jobid. Hold the lock for as short a time as possible. +***************************************************************************/ -static int get_total_jobs(void) +static BOOL allocate_print_jobid(struct tdb_print_db *pdb, int snum, const char *printername, uint32 *pjobid) { - int total_jobs = 0; - int snum; - int services = lp_numservices(); + int i; + uint32 jobid; - for (snum = 0; snum < services; snum++) { - struct tdb_print_db *pdb; - int jobs; + *pjobid = (uint32)-1; - if (!lp_print_ok(snum)) - continue; + for (i = 0; i < 3; i++) { + /* Lock the database - only wait 20 seconds. */ + if (tdb_lock_bystring(pdb->tdb, "INFO/nextjob", 20) == -1) { + DEBUG(0,("allocate_print_jobid: failed to lock printing database %s\n", printername )); + return False; + } - pdb = get_print_db_byname(lp_const_servicename(snum)); - if (!pdb) - continue; + 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", + printername )); + return False; + } + jobid = 0; + } - /* make sure the database is up to date */ - if (print_cache_expired(snum)) - print_queue_update(snum); + jobid = NEXT_JOBID(jobid); - jobs = tdb_fetch_int32(pdb->tdb, "INFO/total_jobs"); - if (jobs > 0) - total_jobs += jobs; + if (tdb_store_int32(pdb->tdb, "INFO/nextjob", jobid)==-1) { + DEBUG(3, ("allocate_print_jobid: failed to store INFO/nextjob.\n")); + tdb_unlock_bystring(pdb->tdb, "INFO/nextjob"); + return False; + } + + /* We've finished with the INFO/nextjob lock. */ + tdb_unlock_bystring(pdb->tdb, "INFO/nextjob"); + + if (!print_job_exists(snum, jobid)) + break; + } + + if (i > 2) { + DEBUG(0, ("allocate_print_jobid: failed to allocate a print job for queue %s\n", + printername )); + /* Probably full... */ + errno = ENOSPC; + return False; } - return total_jobs; + + /* Store a dummy placeholder. */ + { + TDB_DATA dum; + dum.dptr = NULL; + dum.dsize = 0; + if (tdb_store(pdb->tdb, print_key(jobid), dum, TDB_INSERT) == -1) { + DEBUG(3, ("allocate_print_jobid: jobid (%d) failed to store placeholder.\n", + jobid )); + return False; + } + } + + *pjobid = jobid; + return True; +} + +/*************************************************************************** + Append a jobid to the 'jobs changed' list. +***************************************************************************/ + +static BOOL add_to_jobs_changed(struct tdb_print_db *pdb, uint32 jobid) +{ + TDB_DATA data, key; + + key.dptr = "INFO/jobs_changed"; + key.dsize = strlen(key.dptr); + data.dptr = (char *)&jobid; + data.dsize = 4; + + DEBUG(10,("add_to_jobs_changed: Added jobid %u\n", (unsigned int)jobid )); + + return (tdb_append(pdb->tdb, key, data) == 0); } /*************************************************************************** Start spooling a job - return the jobid. ***************************************************************************/ -uint32 print_job_start(struct current_user *user, int snum, char *jobname) +uint32 print_job_start(struct current_user *user, int snum, char *jobname, NT_DEVICEMODE *nt_devmode ) { uint32 jobid; char *path; struct printjob pjob; - int next_jobid; user_struct *vuser; - int njobs = 0; const char *printername = lp_const_servicename(snum); struct tdb_print_db *pdb = get_print_db_byname(printername); + int njobs; errno = 0; @@ -1313,11 +1889,13 @@ uint32 print_job_start(struct current_user *user, int snum, char *jobname) 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_time_access_check(snum)) { DEBUG(3, ("print_job_start: job start denied by time check\n")); + release_print_db(pdb); return (uint32)-1; } @@ -1329,6 +1907,7 @@ uint32 print_job_start(struct current_user *user, int snum, char *jobname) 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; } @@ -1337,28 +1916,30 @@ uint32 print_job_start(struct current_user *user, int snum, char *jobname) /* for autoloaded printers, check that the printcap entry still exists */ if (lp_autoloaded(snum) && !pcap_printername_ok(lp_const_servicename(snum), NULL)) { DEBUG(3, ("print_job_start: printer name %s check failed.\n", lp_const_servicename(snum) )); + release_print_db(pdb); errno = ENOENT; return (uint32)-1; } /* Insure the maximum queue size is not violated */ - if (lp_maxprintjobs(snum) && (njobs = print_queue_length(snum,NULL)) > lp_maxprintjobs(snum)) { - DEBUG(3, ("print_job_start: number of jobs (%d) larger than max printjobs per queue (%d).\n", - njobs, lp_maxprintjobs(snum) )); + 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", + printername, njobs, lp_maxprintjobs(snum) )); + release_print_db(pdb); errno = ENOSPC; return (uint32)-1; } - /* Insure the maximum print jobs in the system is not violated */ - if (lp_totalprintjobs() && get_total_jobs() > lp_totalprintjobs()) { - DEBUG(3, ("print_job_start: number of jobs (%d) larger than max printjobs per system (%d).\n", - njobs, lp_totalprintjobs() )); - errno = ENOSPC; - return (uint32)-1; - } + DEBUG(10,("print_job_start: Queue %s number of jobs (%d), max printjobs = %d\n", + printername, njobs, lp_maxprintjobs(snum) )); + + if (!allocate_print_jobid(pdb, snum, printername, &jobid)) + goto fail; /* create the database entry */ + ZERO_STRUCT(pjob); + pjob.pid = local_pid; pjob.sysjob = -1; pjob.fd = -1; @@ -1367,7 +1948,8 @@ uint32 print_job_start(struct current_user *user, int snum, char *jobname) 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) { @@ -1378,33 +1960,6 @@ uint32 print_job_start(struct current_user *user, int snum, char *jobname) fstrcpy(pjob.queuename, lp_const_servicename(snum)); - /* lock the database */ - if (pdb_entry_lock(pdb, "INFO/nextjob") == -1) { - DEBUG(0,("print_job_start: failed to lock printing database %s\n", printername )); - return (uint32)-1; - } - - next_jobid = tdb_fetch_int32(pdb->tdb, "INFO/nextjob"); - if (next_jobid == -1) - next_jobid = 1; - - for (jobid = NEXT_JOBID(next_jobid); jobid != next_jobid; jobid = NEXT_JOBID(jobid)) { - if (!print_job_exists(snum, jobid)) - break; - } - if (jobid == next_jobid || !pjob_store(snum, jobid, &pjob)) { - DEBUG(3, ("print_job_start: either jobid (%d)==next_jobid(%d) or pjob_store failed.\n", - jobid, next_jobid )); - jobid = -1; - goto fail; - } - - if (tdb_store_int32(pdb->tdb, "INFO/nextjob", jobid)==-1) { - DEBUG(3, ("print_job_start: failed to store INFO/nextjob.\n")); - jobid = -1; - goto fail; - } - /* 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); @@ -1425,18 +1980,13 @@ to open spool file %s.\n", pjob.filename)); pjob_store(snum, jobid, &pjob); - pdb_entry_unlock(pdb, "INFO/nextjob"); + /* Update the 'jobs changed' entry used by print_queue_status. */ + add_to_jobs_changed(pdb, jobid); - /* - * If the printer is marked as postscript output a leading - * file identifier to ensure the file is treated as a raw - * postscript file. - * This has a similar effect as CtrlD=0 in WIN.INI file. - * tim@fsg.com 09/06/94 - */ - if (lp_postscript(snum)) { - print_job_write(snum, jobid, "%!\n",3); - } + /* Ensure we keep a rough count of the number of total jobs... */ + tdb_change_int32_atomic(pdb->tdb, "INFO/total_jobs", &njobs, 1); + + release_print_db(pdb); return jobid; @@ -1444,10 +1994,10 @@ to open spool file %s.\n", pjob.filename)); if (jobid != -1) pjob_delete(snum, jobid); - pdb_entry_unlock(pdb, "INFO/nextjob"); + release_print_db(pdb); DEBUG(3, ("print_job_start: returning fail. Error = %s\n", strerror(errno) )); - return -1; + return (uint32)-1; } /**************************************************************************** @@ -1536,126 +2086,169 @@ fail: /* Still need to add proper error return propagation! 010122:JRR */ unlink(pjob->filename); pjob_delete(snum, jobid); + remove_from_jobs_changed(snum, jobid); return False; } /**************************************************************************** - Utility fn to enumerate the print queue. + Get a snapshot of jobs in the system without traversing. ****************************************************************************/ -static int traverse_fn_queue(TDB_CONTEXT *t, TDB_DATA key, TDB_DATA data, void *state) +static BOOL get_stored_queue_info(struct tdb_print_db *pdb, int snum, int *pcount, print_queue_struct **ppqueue) { - struct traverse_struct *ts = (struct traverse_struct *)state; - struct printjob pjob; - int i; - uint32 jobid; + TDB_DATA data, key, cgdata; + print_queue_struct *queue = NULL; + uint32 qcount = 0; + uint32 extra_count = 0; + int total_count = 0; + size_t len = 0; + uint32 i; + int max_reported_jobs = lp_max_reported_jobs(snum); + BOOL ret = False; - if (data.dsize != sizeof(pjob) || key.dsize != sizeof(int)) - return 0; - memcpy(&jobid, key.dptr, sizeof(jobid)); - memcpy(&pjob, data.dptr, sizeof(pjob)); + /* make sure the database is up to date */ + if (print_cache_expired(snum)) + print_queue_update(snum); + + *pcount = 0; + *ppqueue = NULL; - /* maybe it isn't for this queue */ - if (ts->snum != lp_servicenumber(pjob.queuename)) - return 0; + ZERO_STRUCT(data); + ZERO_STRUCT(cgdata); + key.dptr = "INFO/linear_queue_array"; + key.dsize = strlen(key.dptr); - if (ts->qcount >= ts->maxcount) - return 0; + /* Get the stored queue data. */ + data = tdb_fetch(pdb->tdb, key); - i = ts->qcount; + if (data.dptr == NULL || data.dsize < 4) + qcount = 0; + else + memcpy(&qcount, data.dptr, 4); - 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; - ts->queue[i].priority = 1; - ts->queue[i].time = pjob.starttime; - fstrcpy(ts->queue[i].fs_user, pjob.user); - fstrcpy(ts->queue[i].fs_file, pjob.jobname); + /* Get the changed jobs list. */ + key.dptr = "INFO/jobs_changed"; + key.dsize = strlen(key.dptr); - ts->qcount++; + cgdata = tdb_fetch(pdb->tdb, key); + if (cgdata.dptr != NULL && (cgdata.dsize % 4 == 0)) + extra_count = cgdata.dsize/4; - return 0; -} + DEBUG(5,("get_stored_queue_info: qcount = %u, extra_count = %u\n", (unsigned int)qcount, (unsigned int)extra_count)); -struct traverse_count_struct { - int snum, count; -}; + /* Allocate the queue size. */ + if (qcount == 0 && extra_count == 0) + goto out; + + if ((queue = (print_queue_struct *)malloc(sizeof(print_queue_struct)*(qcount + extra_count))) == NULL) + goto out; + + /* Retrieve the linearised queue data. */ + len = 0; + for( i = 0; i < qcount; i++) { + uint32 qjob, qsize, qpage_count, qstatus, qpriority, qtime; + len += tdb_unpack(data.dptr + 4 + len, data.dsize - len, "ddddddff", + &qjob, + &qsize, + &qpage_count, + &qstatus, + &qpriority, + &qtime, + queue[i].fs_user, + queue[i].fs_file); + queue[i].job = qjob; + queue[i].size = qsize; + queue[i].page_count = qpage_count; + queue[i].status = qstatus; + queue[i].priority = qpriority; + queue[i].time = qtime; + } -/**************************************************************************** - Utility fn to count the number of entries in the print queue. -****************************************************************************/ + total_count = qcount; -static int traverse_count_fn_queue(TDB_CONTEXT *t, TDB_DATA key, TDB_DATA data, void *state) -{ - struct traverse_count_struct *ts = (struct traverse_count_struct *)state; - struct printjob pjob; - uint32 jobid; + /* Add in the changed jobids. */ + for( i = 0; i < extra_count; i++) { + uint32 jobid; + struct printjob *pjob; - if (data.dsize != sizeof(pjob) || key.dsize != sizeof(int)) - return 0; - memcpy(&jobid, key.dptr, sizeof(jobid)); - memcpy(&pjob, data.dptr, sizeof(pjob)); + memcpy(&jobid, &cgdata.dptr[i*4], 4); + DEBUG(5,("get_stored_queue_info: changed job = %u\n", (unsigned int)jobid)); + pjob = print_job_find(snum, jobid); + if (!pjob) { + DEBUG(5,("get_stored_queue_info: failed to find changed job = %u\n", (unsigned int)jobid)); + remove_from_jobs_changed(snum, jobid); + continue; + } - /* maybe it isn't for this queue - this cannot happen with the tdb/printer code. JRA */ - if (ts->snum != lp_servicenumber(pjob.queuename)) - return 0; + queue[total_count].job = jobid; + queue[total_count].size = pjob->size; + queue[total_count].page_count = pjob->page_count; + queue[total_count].status = pjob->status; + queue[total_count].priority = 1; + queue[total_count].time = pjob->starttime; + fstrcpy(queue[total_count].fs_user, pjob->user); + fstrcpy(queue[total_count].fs_file, pjob->jobname); + total_count++; + } - ts->count++; + /* Sort the queue by submission time otherwise they are displayed + in hash order. */ - return 0; -} + qsort(queue, total_count, sizeof(print_queue_struct), QSORT_CAST(printjob_comp)); -/**************************************************************************** - Sort print jobs by submittal time. -****************************************************************************/ + DEBUG(5,("get_stored_queue_info: total_count = %u\n", (unsigned int)total_count)); -static int printjob_comp(print_queue_struct *j1, print_queue_struct *j2) -{ - /* Silly cases */ + if (max_reported_jobs && total_count > max_reported_jobs) + total_count = max_reported_jobs; - if (!j1 && !j2) - return 0; - if (!j1) - return -1; - if (!j2) - return 1; + *ppqueue = queue; + *pcount = total_count; - /* Sort on job start time */ + ret = True; - if (j1->time == j2->time) - return 0; - return (j1->time > j2->time) ? 1 : -1; + out: + + SAFE_FREE(data.dptr); + SAFE_FREE(cgdata.dptr); + return ret; } /**************************************************************************** Get a printer queue listing. + set queue = NULL and status = NULL if you just want to update the cache ****************************************************************************/ int print_queue_status(int snum, - print_queue_struct **queue, + print_queue_struct **ppqueue, print_status_struct *status) { - struct traverse_struct tstruct; - struct traverse_count_struct tsc; fstring keystr; TDB_DATA data, key; - const char *printername = lp_const_servicename(snum); - struct tdb_print_db *pdb = get_print_db_byname(printername); - - *queue = NULL; - - if (!pdb) - return 0; + const char *printername; + struct tdb_print_db *pdb; + int count = 0; /* make sure the database is up to date */ + if (print_cache_expired(snum)) print_queue_update(snum); + /* return if we are done */ + if ( !ppqueue || !status ) + return 0; + + *ppqueue = NULL; + printername = lp_const_servicename(snum); + pdb = get_print_db_byname(printername); + + if (!pdb) + return 0; + /* * Fetch the queue status. We must do this first, as there may * be no jobs in the queue. */ + ZERO_STRUCTP(status); slprintf(keystr, sizeof(keystr)-1, "STATUS/%s", printername); key.dptr = keystr; @@ -1672,50 +2265,14 @@ int print_queue_status(int snum, * Now, fetch the print queue information. We first count the number * of entries, and then only retrieve the queue if necessary. */ - tsc.count = 0; - tsc.snum = snum; - - tdb_traverse(pdb->tdb, traverse_count_fn_queue, (void *)&tsc); - - if (tsc.count == 0) - return 0; - /* Allocate the queue size. */ - if ((tstruct.queue = (print_queue_struct *) - malloc(sizeof(print_queue_struct)*tsc.count)) == NULL) + if (!get_stored_queue_info(pdb, snum, &count, ppqueue)) { + release_print_db(pdb); return 0; + } - /* - * Fill in the queue. - * We need maxcount as the queue size may have changed between - * the two calls to tdb_traverse. - */ - tstruct.qcount = 0; - tstruct.maxcount = tsc.count; - tstruct.snum = snum; - - tdb_traverse(pdb->tdb, traverse_fn_queue, (void *)&tstruct); - - /* Sort the queue by submission time otherwise they are displayed - in hash order. */ - - qsort(tstruct.queue, tstruct.qcount, sizeof(print_queue_struct), - QSORT_CAST(printjob_comp)); - - *queue = tstruct.queue; - return tstruct.qcount; -} - -/**************************************************************************** - Turn a queue name into a snum. -****************************************************************************/ - -int print_queue_snum(const char *qname) -{ - int snum = lp_servicenumber(qname); - if (snum == -1 || !lp_print_ok(snum)) - return -1; - return snum; + release_print_db(pdb); + return count; } /****************************************************************************