summaryrefslogtreecommitdiff
path: root/source3/printing/printing.c
diff options
context:
space:
mode:
Diffstat (limited to 'source3/printing/printing.c')
-rw-r--r--source3/printing/printing.c2229
1 files changed, 1397 insertions, 832 deletions
diff --git a/source3/printing/printing.c b/source3/printing/printing.c
index 1dd8921800..ad5acb1505 100644
--- a/source3/printing/printing.c
+++ b/source3/printing/printing.c
@@ -1,8 +1,7 @@
/*
- Unix SMB/Netbios implementation.
- Version 1.9.
- printing routines
- Copyright (C) Andrew Tridgell 1992-1995
+ Unix SMB/CIFS implementation.
+ printing backend routines
+ Copyright (C) Andrew Tridgell 1992-2000
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
@@ -19,841 +18,1407 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#include "includes.h"
-#include "loadparm.h"
-extern int DEBUGLEVEL;
-extern connection_struct Connections[];
-extern files_struct Files[];
-
-static BOOL * lpq_cache_reset=NULL;
-
-static int check_lpq_cache(int snum) {
- static int lpq_caches=0;
-
- if (lpq_caches <= snum) {
- BOOL * p;
- p = (BOOL *) Realloc(lpq_cache_reset,(snum+1)*sizeof(BOOL));
- if (p) {
- lpq_cache_reset=p;
- lpq_caches = snum+1;
- }
- }
- return lpq_caches;
-}
-
-void lpq_reset(int snum)
-{
- if (check_lpq_cache(snum) > snum) lpq_cache_reset[snum]=True;
-}
-
-
-/****************************************************************************
-Build the print command in the supplied buffer. This means getting the
-print command for the service and inserting the printer name and the
-print file name. Return NULL on error, else the passed buffer pointer.
-****************************************************************************/
-static char *build_print_command(int cnum, char *command, char *syscmd, char *filename1)
-{
- int snum = SNUM(cnum);
- char *tstr;
- pstring filename;
-
- /* get the print command for the service. */
- tstr = command;
- if (!syscmd || !tstr) {
- DEBUG(0,("No print command for service `%s'\n", SERVICE(snum)));
- return (NULL);
- }
-
- /* copy the command into the buffer for extensive meddling. */
- StrnCpy(syscmd, tstr, sizeof(pstring) - 1);
-
- /* look for "%s" in the string. If there is no %s, we cannot print. */
- if (!strstr(syscmd, "%s") && !strstr(syscmd, "%f")) {
- DEBUG(2,("WARNING! No placeholder for the filename in the print command for service %s!\n", SERVICE(snum)));
- }
-
- if (strstr(syscmd,"%s")) {
- int iOffset = strstr(syscmd, "%s") - syscmd;
-
- /* construct the full path for the filename, shouldn't be necessary unless
- the subshell causes a "cd" to be executed.
- Only use the full path if there isn't a / preceding the %s */
- if (iOffset==0 || syscmd[iOffset-1] != '/') {
- StrnCpy(filename,Connections[cnum].connectpath,sizeof(filename)-1);
- trim_string(filename,"","/");
- strcat(filename,"/");
- strcat(filename,filename1);
- }
- else
- strcpy(filename,filename1);
-
- string_sub(syscmd, "%s", filename);
- }
-
- string_sub(syscmd, "%f", filename1);
-
- /* Does the service have a printername? If not, make a fake and empty */
- /* printer name. That way a %p is treated sanely if no printer */
- /* name was specified to replace it. This eventuality is logged. */
- tstr = PRINTERNAME(snum);
- if (tstr == NULL || tstr[0] == '\0') {
- DEBUG(3,( "No printer name - using %s.\n", SERVICE(snum)));
- tstr = SERVICE(snum);
- }
-
- string_sub(syscmd, "%p", tstr);
-
- standard_sub(cnum,syscmd);
-
- return (syscmd);
-}
-
-
-/****************************************************************************
-print a file - called on closing the file
-****************************************************************************/
-void print_file(int fnum)
-{
- pstring syscmd;
- int cnum = Files[fnum].cnum;
- int snum=SNUM(cnum);
- char *tempstr;
-
- *syscmd = 0;
-
- if (file_size(Files[fnum].name) <= 0) {
- DEBUG(3,("Discarding null print job %s\n",Files[fnum].name));
- sys_unlink(Files[fnum].name);
- return;
- }
-
- tempstr = build_print_command(cnum, PRINTCOMMAND(snum), syscmd, Files[fnum].name);
- if (tempstr != NULL)
- {
- int ret = smbrun(syscmd,NULL);
- DEBUG(3,("Running the command `%s' gave %d\n",syscmd,ret));
- }
- else
- DEBUG(0,("Null print command?\n"));
-
- lpq_reset(snum);
-}
-
-static char *Months[13] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
- "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "Err"};
-
-
-/*******************************************************************
-process time fields
-********************************************************************/
-static time_t EntryTime(string tok[], int ptr, int count, int minimum)
-{
- time_t jobtime;
-
- jobtime = time(NULL); /* default case: take current time */
- if (count >= minimum) {
- struct tm *t;
- int i, day, hour, min, sec;
- char *c;
-
- for (i=0; i<13; i++) if (!strncmp(tok[ptr], Months[i],3)) break; /* Find month */
- if (i<12) {
- t = localtime(&jobtime);
- day = atoi(tok[ptr+1]);
- c=(char *)(tok[ptr+2]);
- *(c+2)=0;
- hour = atoi(c);
- *(c+5)=0;
- min = atoi(c+3);
- if(*(c+6) != 0)sec = atoi(c+6);
- else sec=0;
-
- if ((t->tm_mon < i)||
- ((t->tm_mon == i)&&
- ((t->tm_mday < day)||
- ((t->tm_mday == day)&&
- (t->tm_hour*60+t->tm_min < hour*60+min)))))
- t->tm_year--; /* last year's print job */
-
- t->tm_mon = i;
- t->tm_mday = day;
- t->tm_hour = hour;
- t->tm_min = min;
- t->tm_sec = sec;
- jobtime = mktime(t);
- }
- }
- return jobtime;
-}
-
-
-/****************************************************************************
-parse a lpq line
-
-here is an example of lpq output under bsd
-
-Warning: no daemon present
-Rank Owner Job Files Total Size
-1st tridge 148 README 8096 bytes
-
-here is an example of lpq output under osf/1
-
-Warning: no daemon present
-Rank Pri Owner Job Files Total Size
-1st 0 tridge 148 README 8096 bytes
-****************************************************************************/
-static BOOL parse_lpq_bsd(char *line,print_queue_struct *buf,BOOL first)
-{
-#ifdef OSF1
-#define RANKTOK 0
-#define PRIOTOK 1
-#define USERTOK 2
-#define JOBTOK 3
-#define FILETOK 4
-#define TOTALTOK 5
-#define NTOK 6
-#else /* OSF1 */
-#define RANKTOK 0
-#define USERTOK 1
-#define JOBTOK 2
-#define FILETOK 3
-#define TOTALTOK 4
-#define NTOK 5
-#endif /* OSF1 */
-
- string tok[NTOK];
- int count=0;
-
-#ifdef OSF1
- int length;
- length = strlen(line);
- if (line[length-3] == ':')
- return(False);
-#endif /* OSF1 */
-
- /* handle the case of "(standard input)" as a filename */
- string_sub(line,"standard input","STDIN");
- string_sub(line,"(","\"");
- string_sub(line,")","\"");
-
- for (count=0; count<NTOK && next_token(&line,tok[count],NULL); count++) ;
-
- /* we must get NTOK tokens */
- if (count < NTOK)
- return(False);
-
- /* the Job and Total columns must be integer */
- if (!isdigit(*tok[JOBTOK]) || !isdigit(*tok[TOTALTOK])) return(False);
-
- /* if the fname contains a space then use STDIN */
- if (strchr(tok[FILETOK],' '))
- strcpy(tok[FILETOK],"STDIN");
-
- /* only take the last part of the filename */
- {
- string tmp;
- char *p = strrchr(tok[FILETOK],'/');
- if (p)
- {
- strcpy(tmp,p+1);
- strcpy(tok[FILETOK],tmp);
- }
- }
+#include "printing.h"
+
+/* Current printer interface */
+struct printif *current_printif = &generic_printif;
+
+/*
+ the printing backend revolves around a tdb database that stores the
+ SMB view of the print queue
+
+ The key for this database is a jobid - a internally generated number that
+ uniquely identifies a print job
+
+ reading the print queue involves two steps:
+ - possibly running lpq and updating the internal database from that
+ - reading entries from the database
+
+ jobids are assigned when a job starts spooling.
+*/
+
+/* the open printing.tdb database */
+static TDB_CONTEXT *tdb;
+static pid_t local_pid;
+
+static int get_queue_status(int, print_status_struct *);
+
+/****************************************************************************
+ Initialise the printing backend. Called once at startup.
+ Does not survive a fork
+****************************************************************************/
+
+BOOL print_backend_init(void)
+{
+ char *sversion = "INFO/version";
+
+ if (tdb && local_pid == sys_getpid()) return True;
+ tdb = tdb_open_log(lock_path("printing.tdb"), 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
+ if (!tdb) {
+ DEBUG(0,("print_backend_init: Failed to open printing backend database. Error = [%s]\n",
+ tdb_errorstr(tdb)));
+ return False;
+ }
+ local_pid = sys_getpid();
+
+ /* handle a Samba upgrade */
+ tdb_lock_bystring(tdb, sversion);
+ if (tdb_fetch_int32(tdb, sversion) != PRINT_DATABASE_VERSION) {
+ tdb_traverse(tdb, tdb_traverse_delete_fn, NULL);
+ tdb_store_int32(tdb, sversion, PRINT_DATABASE_VERSION);
+ }
+ tdb_unlock_bystring(tdb, sversion);
+
+ /* select the appropriate printing interface... */
+#ifdef HAVE_CUPS
+ if (strcmp(lp_printcapname(), "cups") == 0)
+ current_printif = &cups_printif;
+#endif /* HAVE_CUPS */
+
+ /* do NT print initialization... */
+ return nt_printing_init();
+}
+
+/****************************************************************************
+useful function to generate a tdb key
+****************************************************************************/
+static TDB_DATA print_key(int jobid)
+{
+ static int j;
+ TDB_DATA ret;
+
+ j = jobid;
+ ret.dptr = (void *)&j;
+ ret.dsize = sizeof(j);
+ return ret;
+}
+
+/****************************************************************************
+useful function to find a print job in the database
+****************************************************************************/
+static struct printjob *print_job_find(int jobid)
+{
+ static struct printjob pjob;
+ TDB_DATA ret;
+
+ ret = tdb_fetch(tdb, print_key(jobid));
+ if (!ret.dptr || ret.dsize != sizeof(pjob)) return NULL;
+
+ memcpy(&pjob, ret.dptr, sizeof(pjob));
+ free(ret.dptr);
+ return &pjob;
+}
+
+/****************************************************************************
+store a job structure back to the database
+****************************************************************************/
+static BOOL print_job_store(int jobid, struct printjob *pjob)
+{
+ TDB_DATA d;
+ BOOL ret;
+
+ d.dptr = (void *)pjob;
+ d.dsize = sizeof(*pjob);
+ ret = (tdb_store(tdb, print_key(jobid), d, TDB_REPLACE) == 0);
+ return ret;
+}
+
+/****************************************************************************
+parse a file name from the system spooler to generate a jobid
+****************************************************************************/
+static int print_parse_jobid(char *fname)
+{
+ int jobid;
+
+ if (strncmp(fname,PRINT_SPOOL_PREFIX,strlen(PRINT_SPOOL_PREFIX)) != 0) return -1;
+ fname += strlen(PRINT_SPOOL_PREFIX);
+
+ jobid = atoi(fname);
+ if (jobid <= 0) return -1;
+
+ return jobid;
+}
+
+
+/****************************************************************************
+list a unix job in the print database
+****************************************************************************/
+static void print_unix_job(int snum, print_queue_struct *q)
+{
+ int jobid = q->job + UNIX_JOB_START;
+ struct printjob pj, *old_pj;
+
+ /* Preserve the timestamp on an existing unix print job */
+
+ old_pj = print_job_find(jobid);
+
+ ZERO_STRUCT(pj);
+
+ pj.pid = (pid_t)-1;
+ pj.sysjob = q->job;
+ pj.fd = -1;
+ pj.starttime = old_pj ? old_pj->starttime : q->time;
+ 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_servicename(snum));
+
+ print_job_store(jobid, &pj);
+}
+
+
+struct traverse_struct {
+ print_queue_struct *queue;
+ int qcount, snum, maxcount, total_jobs;
+};
+
+/* utility fn to delete any jobs that are no longer active */
+static int traverse_fn_delete(TDB_CONTEXT *t, TDB_DATA key, TDB_DATA data, void *state)
+{
+ struct traverse_struct *ts = (struct traverse_struct *)state;
+ struct printjob pjob;
+ int i, jobid;
+
+ if (data.dsize != sizeof(pjob) || key.dsize != sizeof(int)) return 0;
+ memcpy(&jobid, key.dptr, sizeof(jobid));
+ memcpy(&pjob, data.dptr, sizeof(pjob));
+
+ if (ts->snum != lp_servicenumber(pjob.queuename)) {
+ /* this isn't for the queue we are looking at */
+ ts->total_jobs++;
+ return 0;
+ }
+
+ if (!pjob.smbjob) {
+ /* remove a unix job if it isn't in the system queue any more */
+
+ for (i=0;i<ts->qcount;i++) {
+ if (jobid == ts->queue[i].job + UNIX_JOB_START) break;
+ }
+ if (i == ts->qcount)
+ tdb_delete(tdb, key);
+ else
+ ts->total_jobs++;
+ return 0;
+ }
+
+ /* maybe it hasn't been spooled yet */
+ if (!pjob.spooled) {
+ /* 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))
+ tdb_delete(tdb, key);
+ else
+ ts->total_jobs++;
+ return 0;
+ }
+
+ for (i=0;i<ts->qcount;i++) {
+ int qid = print_parse_jobid(ts->queue[i].fs_file);
+ if (jobid == qid) break;
+ }
+
+ /* The job isn't in the system queue - we have to assume it has
+ 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. */
+
+ if ((cur_t - pjob.starttime) > lp_lpqcachetime())
+ tdb_delete(t, key);
+ else
+ ts->total_jobs++;
+ }
+ else
+ ts->total_jobs++;
+
+ return 0;
+}
+
+/****************************************************************************
+check if the print queue has been updated recently enough
+****************************************************************************/
+static void print_cache_flush(int snum)
+{
+ fstring key;
+ slprintf(key, sizeof(key)-1, "CACHE/%s", lp_servicename(snum));
+ tdb_store_int32(tdb, key, -1);
+}
+
+/****************************************************************************
+ Check if someone already thinks they are doing the update.
+****************************************************************************/
+
+static pid_t get_updating_pid(fstring printer_name)
+{
+ fstring keystr;
+ TDB_DATA data, key;
+ pid_t updating_pid;
+
+ slprintf(keystr, sizeof(keystr)-1, "UPDATING/%s", printer_name);
+ key.dptr = keystr;
+ key.dsize = strlen(keystr);
+
+ data = tdb_fetch(tdb, key);
+ if (!data.dptr || data.dsize != sizeof(pid_t))
+ return (pid_t)-1;
+
+ memcpy(&updating_pid, data.dptr, sizeof(pid_t));
+ free(data.dptr);
+
+ if (process_exists(updating_pid))
+ return updating_pid;
+
+ return (pid_t)-1;
+}
+
+/****************************************************************************
+ Set the fact that we're doing the update, or have finished doing the update
+ in th tdb.
+****************************************************************************/
+
+static void set_updating_pid(fstring printer_name, BOOL delete)
+{
+ fstring keystr;
+ TDB_DATA key;
+ TDB_DATA data;
+ pid_t updating_pid = sys_getpid();
+
+ slprintf(keystr, sizeof(keystr)-1, "UPDATING/%s", printer_name);
+ key.dptr = keystr;
+ key.dsize = strlen(keystr);
+
+ if (delete) {
+ tdb_delete(tdb, key);
+ return;
+ }
+ data.dptr = (void *)&updating_pid;
+ data.dsize = sizeof(pid_t);
+
+ tdb_store(tdb, key, data, TDB_REPLACE);
+}
+
+/****************************************************************************
+ Send a message saying the queue changed.
+****************************************************************************/
+
+static void send_queue_message(const char *printer_name, uint32 high, uint32 low)
+{
+ char msg[8 + sizeof(fstring)];
+ SIVAL(msg,0,low);
+ SIVAL(msg,4,high);
+ fstrcpy(&msg[8], printer_name);
+
+ message_send_all(conn_tdb_ctx(), MSG_PRINTER_NOTIFY, msg, 8 + strlen(printer_name) + 1, False, NULL);
+}
+
+/****************************************************************************
+update the internal database from the system print queue for a queue in the background
+****************************************************************************/
+
+static void print_queue_update_background(int snum)
+{
+ int i, qcount;
+ print_queue_struct *queue = NULL;
+ print_status_struct status;
+ print_status_struct old_status;
+ struct printjob *pjob;
+ struct traverse_struct tstruct;
+ fstring keystr, printer_name, cachestr;
+ TDB_DATA data, key;
+
+ fstrcpy(printer_name, lp_servicename(snum));
+
+ /*
+ * Check to see if someone else is doing this update.
+ * This is essentially a mutex on the update.
+ */
+
+ if (get_updating_pid(printer_name) != -1)
+ return;
+
+ /* Lock the queue for the database update */
+
+ slprintf(keystr, sizeof(keystr) - 1, "LOCK/%s", printer_name);
+ tdb_lock_bystring(tdb, keystr);
+
+ /*
+ * Ensure that no one else got in here.
+ * If the updating pid is still -1 then we are
+ * the winner.
+ */
+
+ if (get_updating_pid(printer_name) != -1) {
+ /*
+ * Someone else is doing the update, exit.
+ */
+ tdb_unlock_bystring(tdb, keystr);
+ return;
+ }
+
+ /*
+ * We're going to do the update ourselves.
+ */
+
+ /* Tell others we're doing the update. */
+ set_updating_pid(printer_name, False);
+
+ /*
+ * Allow others to enter and notice we're doing
+ * the update.
+ */
+
+ tdb_unlock_bystring(tdb, keystr);
+
+ /*
+ * Update the cache time FIRST ! Stops others even
+ * attempting to get the lock and doing this
+ * if the lpq takes a long time.
+ */
+
+ slprintf(cachestr, sizeof(cachestr)-1, "CACHE/%s", printer_name);
+ tdb_store_int32(tdb, cachestr, (int)time(NULL));
- buf->job = atoi(tok[JOBTOK]);
- buf->size = atoi(tok[TOTALTOK]);
- buf->status = strequal(tok[RANKTOK],"active")?LPQ_PRINTING:LPQ_QUEUED;
- buf->time = time(NULL);
- StrnCpy(buf->user,tok[USERTOK],sizeof(buf->user)-1);
- StrnCpy(buf->file,tok[FILETOK],sizeof(buf->file)-1);
-#ifdef PRIOTOK
- buf->priority = atoi(tok[PRIOTOK]);
-#else
- buf->priority = 1;
-#endif
- return(True);
-}
-
-
-
-/*******************************************************************
-parse lpq on an aix system
-
-Queue Dev Status Job Files User PP % Blks Cp Rnk
-------- ----- --------- --- ------------------ ---------- ---- -- ----- --- ---
-lazer lazer READY
-lazer lazer RUNNING 537 6297doc.A kvintus@IE 0 10 2445 1 1
- QUEUED 538 C.ps root@IEDVB 124 1 2
- QUEUED 539 E.ps root@IEDVB 28 1 3
- QUEUED 540 L.ps root@IEDVB 172 1 4
- QUEUED 541 P.ps root@IEDVB 22 1 5
-********************************************************************/
-static BOOL parse_lpq_aix(char *line,print_queue_struct *buf,BOOL first)
-{
- string tok[11];
- int count=0;
-
- /* handle the case of "(standard input)" as a filename */
- string_sub(line,"standard input","STDIN");
- string_sub(line,"(","\"");
- string_sub(line,")","\"");
-
- for (count=0; count<10 && next_token(&line,tok[count],NULL); count++) ;
-
- /* we must get 6 tokens */
- if (count < 10)
- {
- if ((count == 7) && (strcmp(tok[0],"QUEUED") == 0))
- {
- /* the 2nd and 5th columns must be integer */
- if (!isdigit(*tok[1]) || !isdigit(*tok[4])) return(False);
- buf->size = atoi(tok[4]) * 1024;
- /* if the fname contains a space then use STDIN */
- if (strchr(tok[2],' '))
- strcpy(tok[2],"STDIN");
-
- /* only take the last part of the filename */
- {
- string tmp;
- char *p = strrchr(tok[2],'/');
- if (p)
- {
- strcpy(tmp,p+1);
- strcpy(tok[2],tmp);
- }
- }
-
-
- buf->job = atoi(tok[1]);
- buf->status = LPQ_QUEUED;
- buf->priority = 0;
- buf->time = time(NULL);
- StrnCpy(buf->user,tok[3],sizeof(buf->user)-1);
- StrnCpy(buf->file,tok[2],sizeof(buf->file)-1);
- }
- else
- {
- DEBUG(6,("parse_lpq_aix count=%d\n", count));
- return(False);
- }
- }
- else
- {
- /* the 4th and 9th columns must be integer */
- if (!isdigit(*tok[3]) || !isdigit(*tok[8])) return(False);
- buf->size = atoi(tok[8]) * 1024;
- /* if the fname contains a space then use STDIN */
- if (strchr(tok[4],' '))
- strcpy(tok[4],"STDIN");
-
- /* only take the last part of the filename */
- {
- string tmp;
- char *p = strrchr(tok[4],'/');
- if (p)
- {
- strcpy(tmp,p+1);
- strcpy(tok[4],tmp);
- }
- }
-
-
- buf->job = atoi(tok[3]);
- buf->status = strequal(tok[2],"RUNNING")?LPQ_PRINTING:LPQ_QUEUED;
- buf->priority = 0;
- buf->time = time(NULL);
- StrnCpy(buf->user,tok[5],sizeof(buf->user)-1);
- StrnCpy(buf->file,tok[4],sizeof(buf->file)-1);
- }
-
-
- return(True);
-}
-
-
-/****************************************************************************
-parse a lpq line
-here is an example of lpq output under hpux; note there's no space after -o !
-$> lpstat -oljplus
-ljplus-2153 user priority 0 Jan 19 08:14 on ljplus
- util.c 125697 bytes
- server.c 110712 bytes
-ljplus-2154 user priority 0 Jan 19 08:14 from client
- (standard input) 7551 bytes
-****************************************************************************/
-static BOOL parse_lpq_hpux(char * line, print_queue_struct *buf, BOOL first)
-{
- /* must read two lines to process, therefore keep some values static */
- static BOOL header_line_ok=False, base_prio_reset=False;
- static string jobuser;
- static int jobid;
- static int jobprio;
- static time_t jobtime;
- static int jobstat=LPQ_QUEUED;
- /* to store minimum priority to print, lpstat command should be invoked
- with -p option first, to work */
- static int base_prio;
+ /* get the current queue using the appropriate interface */
+ ZERO_STRUCT(status);
+
+ qcount = (*(current_printif->queue_get))(snum, &queue, &status);
+
+ DEBUG(3, ("%d job%s in queue for %s\n", qcount, (qcount != 1) ?
+ "s" : "", printer_name));
+
+ /*
+ 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
+ is added as a unix job
+
+ fill in any system job numbers as we go
+ */
+ for (i=0; i<qcount; i++) {
+ int jobid = print_parse_jobid(queue[i].fs_file);
+
+ if (jobid == -1) {
+ /* assume its a unix print job */
+ print_unix_job(snum, &queue[i]);
+ continue;
+ }
+
+ /* we have an active SMB print job - update its status */
+ pjob = print_job_find(jobid);
+ if (!pjob) {
+ /* err, somethings wrong. Probably smbd was restarted
+ with jobs in the queue. All we can do is treat them
+ like unix jobs. Pity. */
+ print_unix_job(snum, &queue[i]);
+ continue;
+ }
+
+ pjob->sysjob = queue[i].job;
+ pjob->status = queue[i].status;
+
+ print_job_store(jobid, pjob);
+ }
+
+ /* 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;
+
+ tdb_traverse(tdb, traverse_fn_delete, (void *)&tstruct);
+
+ safe_free(tstruct.queue);
+
+ tdb_store_int32(tdb, "INFO/total_jobs", tstruct.total_jobs);
+
+ /*
+ * Get the old print status. We will use this to compare the
+ * number of jobs. If they have changed we need to send a
+ * "changed" message to the smbds.
+ */
+
+ if( qcount != get_queue_status(snum, &old_status)) {
+ DEBUG(10,("print_queue_update: queue status change %d jobs -> %d jobs for printer %s\n",
+ old_status.qcount, qcount, printer_name ));
+ send_queue_message(printer_name, 0, PRINTER_CHANGE_JOB);
+ }
+
+ /* store the new queue status structure */
+ slprintf(keystr, sizeof(keystr)-1, "STATUS/%s", printer_name);
+ key.dptr = keystr;
+ key.dsize = strlen(keystr);
+
+ status.qcount = qcount;
+ data.dptr = (void *)&status;
+ data.dsize = sizeof(status);
+ tdb_store(tdb, key, data, TDB_REPLACE);
+
+ /*
+ * Update the cache time again. We want to do this call
+ * as little as possible...
+ */
+
+ slprintf(keystr, sizeof(keystr)-1, "CACHE/%s", printer_name);
+ tdb_store_int32(tdb, keystr, (int)time(NULL));
+
+ /* Delete our pid from the db. */
+ set_updating_pid(printer_name, True);
+}
+
+/****************************************************************************
+this is the receive function of the background lpq updater
+****************************************************************************/
+static void print_queue_receive(int msg_type, pid_t src, void *buf, size_t len)
+{
+ int snum;
+ snum=*((int *)buf);
+ print_queue_update_background(snum);
+}
+
+static pid_t background_lpq_updater_pid;
+
+/****************************************************************************
+main thread of the background lpq updater
+****************************************************************************/
+void start_background_queue(void)
+{
+ DEBUG(3,("start_background_queue: Starting background LPQ thread\n"));
+ background_lpq_updater_pid = sys_fork();
+
+ if (background_lpq_updater_pid == -1) {
+ DEBUG(5,("start_background_queue: background LPQ thread failed to start. %s\n", strerror(errno) ));
+ exit(1);
+ }
+
+ if(background_lpq_updater_pid == 0) {
+ /* Child. */
+ DEBUG(5,("start_background_queue: background LPQ thread started\n"));
+
+ claim_connection(NULL,"smbd lpq backend",0,False);
+
+ if (!locking_init(0))
+ exit(1);
+
+ if (!print_backend_init())
+ exit(1);
+
+ message_register(MSG_PRINTER_UPDATE, print_queue_receive);
+
+ DEBUG(5,("start_background_queue: background LPQ thread waiting for messages\n"));
+ while (1) {
+ pause();
+ DEBUG(10,("start_background_queue: background LPQ thread got a message\n"));
+ message_dispatch();
+ }
+ }
+}
+
+/****************************************************************************
+update the internal database from the system print queue for a queue
+****************************************************************************/
+static void print_queue_update(int snum)
+{
+ message_send_pid(background_lpq_updater_pid, MSG_PRINTER_UPDATE, &snum, sizeof(snum), False);
+}
+
+/****************************************************************************
+check if a jobid is valid. It is valid if it exists in the database
+****************************************************************************/
+BOOL print_job_exists(int jobid)
+{
+ return tdb_exists(tdb, print_key(jobid));
+}
+
+
+/****************************************************************************
+work out which service a jobid is for
+note that we have to look up by queue name to ensure that it works for
+other than the process that started the job
+****************************************************************************/
+int print_job_snum(int jobid)
+{
+ struct printjob *pjob = print_job_find(jobid);
+ if (!pjob) return -1;
+
+ return find_service(pjob->queuename);
+}
+
+/****************************************************************************
+give the fd used for a jobid
+****************************************************************************/
+int print_job_fd(int jobid)
+{
+ struct printjob *pjob = print_job_find(jobid);
+ if (!pjob) return -1;
+ /* don't allow another process to get this info - it is meaningless */
+ if (pjob->pid != local_pid) return -1;
+ return pjob->fd;
+}
+
+/****************************************************************************
+give the filename used for a jobid
+only valid for the process doing the spooling and when the job
+has not been spooled
+****************************************************************************/
+char *print_job_fname(int jobid)
+{
+ struct printjob *pjob = print_job_find(jobid);
+ if (!pjob || pjob->spooled || pjob->pid != local_pid) return NULL;
+ return pjob->filename;
+}
+
+
+/****************************************************************************
+set the place in the queue for a job
+****************************************************************************/
+BOOL print_job_set_place(int jobid, int place)
+{
+ DEBUG(2,("print_job_set_place not implemented yet\n"));
+ return False;
+}
+
+/****************************************************************************
+set the name of a job. Only possible for owner
+****************************************************************************/
+BOOL print_job_set_name(int jobid, char *name)
+{
+ struct printjob *pjob = print_job_find(jobid);
+ if (!pjob || pjob->pid != local_pid) return False;
+
+ fstrcpy(pjob->jobname, name);
+ return print_job_store(jobid, pjob);
+}
+
+
+/****************************************************************************
+delete a print job - don't update queue
+****************************************************************************/
+static BOOL print_job_delete1(int jobid)
+{
+ struct printjob *pjob = print_job_find(jobid);
+ int snum, result = 0;
+
+ if (!pjob) return False;
+
+ /*
+ * If already deleting just return.
+ */
+
+ if (pjob->status == LPQ_DELETING)
+ return True;
+
+ snum = print_job_snum(jobid);
+ if (snum == -1) {
+ DEBUG(5,("print_job_delete1: unknown service number for jobid %d\n", jobid));
+ return False;
+ }
+
+ /* Hrm - we need to be able to cope with deleting a job before it
+ has reached the spooler. */
+
+ if (pjob->sysjob == -1) {
+ DEBUG(5, ("attempt to delete job %d not seen by lpr\n",
+ jobid));
+ }
+
+ /* Set the tdb entry to be deleting. */
+
+ pjob->status = LPQ_DELETING;
+ print_job_store(jobid, pjob);
+
+ if (pjob->spooled && pjob->sysjob != -1)
+ result = (*(current_printif->job_delete))(snum, pjob);
+
+ /* Delete the tdb entry if the delete suceeded or the job hasn't
+ been spooled. */
+
+ if (result == 0) {
+ tdb_delete(tdb, print_key(jobid));
+ }
+
+ return (result == 0);
+}
+
+/****************************************************************************
+return true if the current user owns the print job
+****************************************************************************/
+static BOOL is_owner(struct current_user *user, int jobid)
+{
+ struct printjob *pjob = print_job_find(jobid);
+ user_struct *vuser;
+
+ if (!pjob || !user) 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->uid));
+ }
+}
+
+/****************************************************************************
+delete a print job
+****************************************************************************/
+BOOL print_job_delete(struct current_user *user, int jobid, WERROR *errcode)
+{
+ int snum = print_job_snum(jobid);
+ char *printer_name;
+ BOOL owner;
+
+ if (snum == -1) {
+ DEBUG(5,("print_job_delete: unknown service number for jobid %d\n", jobid));
+ return False;
+ }
+
+ owner = is_owner(user, jobid);
+
+ /* Check access against security descriptor or whether the user
+ owns their job. */
+
+ if (!owner &&
+ !print_access_check(user, snum, JOB_ACCESS_ADMINISTER)) {
+ DEBUG(3, ("delete denied by security descriptor\n"));
+ *errcode = WERR_ACCESS_DENIED;
+ return False;
+ }
+
+ if (!print_job_delete1(jobid)) return False;
+
+ /* force update the database and say the delete failed if the
+ job still exists */
+
+ print_queue_update(snum);
+
+ /* Send a printer notify message */
+
+ printer_name = PRINTERNAME(snum);
+
+ send_queue_message(printer_name, 0, PRINTER_CHANGE_JOB);
+
+ return !print_job_exists(jobid);
+}
+
+
+/****************************************************************************
+pause a job
+****************************************************************************/
+BOOL print_job_pause(struct current_user *user, int jobid, WERROR *errcode)
+{
+ struct printjob *pjob = print_job_find(jobid);
+ int snum, ret = -1;
+ char *printer_name;
+
+ if (!pjob || !user) return False;
+
+ if (!pjob->spooled || pjob->sysjob == -1) return False;
+
+ snum = print_job_snum(jobid);
+ if (snum == -1) {
+ DEBUG(5,("print_job_pause: unknown service number for jobid %d\n", jobid));
+ return False;
+ }
+ if (snum == -1) {
+ DEBUG(5,("print_job_resume: unknown service number for jobid %d\n", jobid));
+ return False;
+ }
+
+ if (!is_owner(user, jobid) &&
+ !print_access_check(user, snum, JOB_ACCESS_ADMINISTER)) {
+ DEBUG(3, ("pause denied by security descriptor\n"));
+ *errcode = WERR_ACCESS_DENIED;
+ return False;
+ }
+
+ /* need to pause the spooled entry */
+ ret = (*(current_printif->job_pause))(snum, pjob);
+
+ if (ret != 0) {
+ *errcode = WERR_INVALID_PARAM;
+ return False;
+ }
+
+ /* force update the database */
+ print_cache_flush(snum);
+
+ /* Send a printer notify message */
+
+ printer_name = PRINTERNAME(snum);
+
+ send_queue_message(printer_name, 0, PRINTER_CHANGE_JOB);
+
+ /* how do we tell if this succeeded? */
+
+ return True;
+}
+
+/****************************************************************************
+resume a job
+****************************************************************************/
+BOOL print_job_resume(struct current_user *user, int jobid, WERROR *errcode)
+{
+ struct printjob *pjob = print_job_find(jobid);
+ char *printer_name;
+ int snum, ret;
+
+ if (!pjob || !user) return False;
+
+ if (!pjob->spooled || pjob->sysjob == -1) return False;
+
+ snum = print_job_snum(jobid);
+
+ if (!is_owner(user, jobid) &&
+ !print_access_check(user, snum, JOB_ACCESS_ADMINISTER)) {
+ DEBUG(3, ("resume denied by security descriptor\n"));
+ *errcode = WERR_ACCESS_DENIED;
+ return False;
+ }
+
+ ret = (*(current_printif->job_resume))(snum, pjob);
+
+ if (ret != 0) {
+ *errcode = WERR_INVALID_PARAM;
+ return False;
+ }
+
+ /* force update the database */
+ print_cache_flush(snum);
+
+ /* Send a printer notify message */
+
+ printer_name = PRINTERNAME(snum);
+
+ send_queue_message(printer_name, 0, PRINTER_CHANGE_JOB);
+
+ return True;
+}
+
+/****************************************************************************
+write to a print file
+****************************************************************************/
+int print_job_write(int jobid, const char *buf, int size)
+{
+ int return_code;
+ struct printjob *pjob = print_job_find(jobid);
+
+ if (!pjob)
+ return -1;
+ /* don't allow another process to get this info - it is meaningless */
+ if (pjob->pid != local_pid)
+ return -1;
+
+ return_code = write(pjob->fd, buf, size);
+ if (return_code>0) {
+ pjob->size += size;
+ print_job_store(jobid, pjob);
+ }
+ return return_code;
+}
+
+/****************************************************************************
+ Check if the print queue has been updated recently enough.
+****************************************************************************/
+
+static BOOL print_cache_expired(int snum)
+{
+ fstring key;
+ time_t last_qscan_time, time_now = time(NULL);
+
+ slprintf(key, sizeof(key), "CACHE/%s", lp_servicename(snum));
+ last_qscan_time = (time_t)tdb_fetch_int32(tdb, key);
+
+ /*
+ * Invalidate the queue for 3 reasons.
+ * (1). last queue scan time == -1.
+ * (2). Current time - last queue scan time > allowed cache time.
+ * (3). last queue scan time > current time + MAX_CACHE_VALID_TIME (1 hour by default).
+ * This last test picks up machines for which the clock has been moved
+ * forward, an lpq scan done and then the clock moved back. Otherwise
+ * 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)) {
+ DEBUG(3, ("print cache expired for queue %s \
+(last_qscan_time = %d, time now = %d, qcachetime = %d)\n", lp_servicename(snum),
+ (int)last_qscan_time, (int)time_now, (int)lp_lpqcachetime() ));
+ return True;
+ }
+ return False;
+}
+
+/****************************************************************************
+ Get the queue status - do not update if db is out of date.
+****************************************************************************/
+static int get_queue_status(int snum, print_status_struct *status)
+{
+ fstring keystr;
+ TDB_DATA data, key;
+
+ ZERO_STRUCTP(status);
+ slprintf(keystr, sizeof(keystr)-1, "STATUS/%s", lp_servicename(snum));
+ key.dptr = keystr;
+ key.dsize = strlen(keystr);
+ data = tdb_fetch(tdb, key);
+ if (data.dptr) {
+ if (data.dsize == sizeof(print_status_struct)) {
+ memcpy(status, data.dptr, sizeof(print_status_struct));
+ }
+ free(data.dptr);
+ }
+ return status->qcount;
+}
+
+/****************************************************************************
+ Determine the number of jobs in a queue.
+****************************************************************************/
+
+int print_queue_length(int snum, print_status_struct *pstatus)
+{
+ print_status_struct status;
+ int len;
- int count;
- char TAB = '\011';
- string tok[12];
-
- /* If a line begins with a horizontal TAB, it is a subline type */
-
- if (line[0] == TAB) { /* subline */
- /* check if it contains the base priority */
- if (!strncmp(line,"\tfence priority : ",18)) {
- base_prio=atoi(&line[18]);
- DEBUG(4, ("fence priority set at %d\n", base_prio));
- }
- if (!header_line_ok) return (False); /* incorrect header line */
- /* handle the case of "(standard input)" as a filename */
- string_sub(line,"standard input","STDIN");
- string_sub(line,"(","\"");
- string_sub(line,")","\"");
-
- for (count=0; count<2 && next_token(&line,tok[count],NULL); count++) ;
- /* we must get 2 tokens */
- if (count < 2) return(False);
-
- /* the 2nd column must be integer */
- if (!isdigit(*tok[1])) return(False);
-
- /* if the fname contains a space then use STDIN */
- if (strchr(tok[0],' '))
- strcpy(tok[0],"STDIN");
-
- buf->size = atoi(tok[1]);
- StrnCpy(buf->file,tok[0],sizeof(buf->file)-1);
-
- /* fill things from header line */
- buf->time = jobtime;
- buf->job = jobid;
- buf->status = jobstat;
- buf->priority = jobprio;
- StrnCpy(buf->user,jobuser,sizeof(buf->user)-1);
-
- return(True);
- }
- else { /* header line */
- header_line_ok=False; /* reset it */
- if (first) {
- if (!base_prio_reset) {
- base_prio=0; /* reset it */
- base_prio_reset=True;
- }
- }
- else if (base_prio) base_prio_reset=False;
-
- /* handle the dash in the job id */
- string_sub(line,"-"," ");
-
- for (count=0; count<12 && next_token(&line,tok[count],NULL); count++) ;
-
- /* we must get 8 tokens */
- if (count < 8) return(False);
-
- /* first token must be printer name (cannot check ?) */
- /* the 2nd, 5th & 7th column must be integer */
- if (!isdigit(*tok[1]) || !isdigit(*tok[4]) || !isdigit(*tok[6])) return(False);
- jobid = atoi(tok[1]);
- StrnCpy(jobuser,tok[2],sizeof(buf->user)-1);
- jobprio = atoi(tok[4]);
-
- /* process time */
- jobtime=EntryTime(tok, 5, count, 8);
- if (jobprio < base_prio) {
- jobstat = LPQ_PAUSED;
- DEBUG (4, ("job %d is paused: prio %d < %d; jobstat=%d\n", jobid, jobprio, base_prio, jobstat));
- }
- else {
- jobstat = LPQ_QUEUED;
- if ((count >8) && (((strequal(tok[8],"on")) ||
- ((strequal(tok[8],"from")) &&
- ((count > 10)&&(strequal(tok[10],"on")))))))
- jobstat = LPQ_PRINTING;
- }
-
- header_line_ok=True; /* information is correct */
- return(False); /* need subline info to include into queuelist */
- }
-}
-
-
-/****************************************************************************
-parse a lpq line
-
-here is an example of "lpstat -o dcslw" output under sysv
-
-dcslw-896 tridge 4712 Dec 20 10:30:30 on dcslw
-dcslw-897 tridge 4712 Dec 20 10:30:30 being held
-
-****************************************************************************/
-static BOOL parse_lpq_sysv(char *line,print_queue_struct *buf,BOOL first)
-{
- string tok[9];
- int count=0;
- char *p;
-
- /* handle the dash in the job id */
- string_sub(line,"-"," ");
-
- for (count=0; count<9 && next_token(&line,tok[count],NULL); count++) ;
-
- /* we must get 7 tokens */
- if (count < 7)
- return(False);
-
- /* the 2nd and 4th, 6th columns must be integer */
- if (!isdigit(*tok[1]) || !isdigit(*tok[3])) return(False);
- if (!isdigit(*tok[5])) return(False);
-
- /* if the user contains a ! then trim the first part of it */
- if ((p=strchr(tok[2],'!')))
- {
- string tmp;
- strcpy(tmp,p+1);
- strcpy(tok[2],tmp);
- }
-
-
- buf->job = atoi(tok[1]);
- buf->size = atoi(tok[3]);
- if (count > 7 && strequal(tok[7],"on"))
- buf->status = LPQ_PRINTING;
- else if (count > 8 && strequal(tok[7],"being") && strequal(tok[8],"held"))
- buf->status = LPQ_PAUSED;
- else
- buf->status = LPQ_QUEUED;
- buf->priority = 0;
- buf->time = EntryTime(tok, 4, count, 7);
- StrnCpy(buf->user,tok[2],sizeof(buf->user)-1);
- StrnCpy(buf->file,tok[2],sizeof(buf->file)-1);
- return(True);
-}
-
-/****************************************************************************
-parse a lpq line
-
-here is an example of lpq output under qnx
-Spooler: /qnx/spooler, on node 1
-Printer: txt (ready)
-0000: root [job #1 ] active 1146 bytes /etc/profile
-0001: root [job #2 ] ready 2378 bytes /etc/install
-0002: root [job #3 ] ready 1146 bytes -- standard input --
-****************************************************************************/
-static BOOL parse_lpq_qnx(char *line,print_queue_struct *buf,BOOL first)
-{
- string tok[7];
- int count=0;
-
- DEBUG(0,("antes [%s]\n", line));
-
- /* handle the case of "-- standard input --" as a filename */
- string_sub(line,"standard input","STDIN");
- DEBUG(0,("despues [%s]\n", line));
- string_sub(line,"-- ","\"");
- string_sub(line," --","\"");
- DEBUG(0,("despues 1 [%s]\n", line));
-
- string_sub(line,"[job #","");
- string_sub(line,"]","");
- DEBUG(0,("despues 2 [%s]\n", line));
-
-
-
- for (count=0; count<7 && next_token(&line,tok[count],NULL); count++) ;
-
- /* we must get 7 tokens */
- if (count < 7)
- return(False);
-
- /* the 3rd and 5th columns must be integer */
- if (!isdigit(*tok[2]) || !isdigit(*tok[4])) return(False);
+ /* make sure the database is up to date */
+ if (print_cache_expired(snum))
+ print_queue_update(snum);
+
+ /* 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.
+****************************************************************************/
+static int get_total_jobs(int snum)
+{
+ int total_jobs;
+
+ /* make sure the database is up to date */
+ if (print_cache_expired(snum)) print_queue_update(snum);
+
+ total_jobs = tdb_fetch_int32(tdb, "INFO/total_jobs");
+ if (total_jobs >0)
+ return total_jobs;
+ else
+ return 0;
+}
+
+/***************************************************************************
+start spooling a job - return the jobid
+***************************************************************************/
+int print_job_start(struct current_user *user, int snum, char *jobname)
+{
+ int jobid;
+ char *path;
+ struct printjob pjob;
+ int next_jobid;
+ user_struct *vuser;
+ int njobs = 0;
+
+ errno = 0;
+
+ if (!print_access_check(user, snum, PRINTER_ACCESS_USE)) {
+ DEBUG(3, ("print_job_start: job start denied by security descriptor\n"));
+ return -1;
+ }
+
+ if (!print_time_access_check(snum)) {
+ DEBUG(3, ("print_job_start: job start denied by time check\n"));
+ return -1;
+ }
+
+ 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"));
+ errno = ENOSPC;
+ return -1;
+ }
+ }
+
+ /* for autoloaded printers, check that the printcap entry still exists */
+ if (lp_autoloaded(snum) && !pcap_printername_ok(lp_servicename(snum), NULL)) {
+ DEBUG(3, ("print_job_start: printer name %s check failed.\n", lp_servicename(snum) ));
+ errno = ENOENT;
+ return -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) ));
+ errno = ENOSPC;
+ return -1;
+ }
+
+ /* Insure the maximum print jobs in the system is not violated */
+ if (lp_totalprintjobs() && get_total_jobs(snum) > 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 -1;
+ }
+
+ /* create the database entry */
+ ZERO_STRUCT(pjob);
+ pjob.pid = local_pid;
+ pjob.sysjob = -1;
+ pjob.fd = -1;
+ pjob.starttime = time(NULL);
+ pjob.status = LPQ_SPOOLING;
+ pjob.size = 0;
+ pjob.spooled = False;
+ pjob.smbjob = True;
+
+ fstrcpy(pjob.jobname, jobname);
+
+ if ((vuser = get_valid_user_struct(user->vuid)) != NULL) {
+ fstrcpy(pjob.user, vuser->user.smb_name);
+ } else {
+ fstrcpy(pjob.user, uidtoname(user->uid));
+ }
+
+ fstrcpy(pjob.queuename, lp_servicename(snum));
+
+ /* lock the database */
+ tdb_lock_bystring(tdb, "INFO/nextjob");
+
+ next_jobid = tdb_fetch_int32(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(jobid))
+ break;
+ }
+ if (jobid == next_jobid || !print_job_store(jobid, &pjob)) {
+ DEBUG(3, ("print_job_start: either jobid (%d)==next_jobid(%d) or print_job_store failed.\n",
+ jobid, next_jobid ));
+ jobid = -1;
+ goto fail;
+ }
+
+ tdb_store_int32(tdb, "INFO/nextjob", jobid);
+
+ /* we have a job entry - now create the spool file */
+ slprintf(pjob.filename, sizeof(pjob.filename)-1, "%s/%s%.6d.XXXXXX",
+ path, PRINT_SPOOL_PREFIX, 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)));
+ }
+ goto fail;
+ }
+
+ print_job_store(jobid, &pjob);
+
+ tdb_unlock_bystring(tdb, "INFO/nextjob");
+
+ /*
+ * 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(jobid, "%!\n",3);
+ }
+
+ return jobid;
+
+ fail:
+ if (jobid != -1) {
+ tdb_delete(tdb, print_key(jobid));
+ }
+
+ tdb_unlock_bystring(tdb, "INFO/nextjob");
+
+ DEBUG(3, ("print_job_start: returning fail. Error = %s\n", strerror(errno) ));
+ return -1;
+}
+
+/****************************************************************************
+ Update the number of pages spooled to jobid
+****************************************************************************/
+
+void print_job_endpage(int jobid)
+{
+ struct printjob *pjob = print_job_find(jobid);
+ if (!pjob)
+ return;
+ /* don't allow another process to get this info - it is meaningless */
+ if (pjob->pid != local_pid)
+ return;
+
+ pjob->page_count++;
+ print_job_store(jobid, pjob);
+}
+
+/****************************************************************************
+ Print a file - called on closing the file. This spools the job.
+ If normal close is false then we're tearing down the jobs - treat as an
+ error.
+****************************************************************************/
+
+BOOL print_job_end(int jobid, BOOL normal_close)
+{
+ struct printjob *pjob = print_job_find(jobid);
+ int snum, ret;
+ SMB_STRUCT_STAT sbuf;
+
+ if (!pjob)
+ return False;
+
+ if (pjob->spooled || pjob->pid != local_pid)
+ return False;
+
+ snum = print_job_snum(jobid);
+ if (snum == -1) {
+ DEBUG(5,("print_job_end: unknown service number for jobid %d\n", jobid));
+ return False;
+ }
+
+ if (normal_close && (sys_fstat(pjob->fd, &sbuf) == 0)) {
+ pjob->size = sbuf.st_size;
+ close(pjob->fd);
+ pjob->fd = -1;
+ } else {
+
+ /*
+ * Not a normal close or we couldn't stat the job file,
+ * so 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 ));
+ goto fail;
+ }
+
+ /* Technically, this is not quit right. If the printer has a separator
+ * page turned on, the NT spooler prints the separator page even if the
+ * print job is 0 bytes. 010215 JRR */
+ if (pjob->size == 0 || pjob->status == LPQ_DELETING) {
+ /* don't bother spooling empty files or something being deleted. */
+ DEBUG(5,("print_job_end: canceling spool of %s (%s)\n",
+ pjob->filename, pjob->size ? "deleted" : "zero length" ));
+ unlink(pjob->filename);
+ tdb_delete(tdb, print_key(jobid));
+ return True;
+ }
+
+ ret = (*(current_printif->job_submit))(snum, pjob);
+
+ if (ret)
+ goto fail;
+
+ /* The print job has been sucessfully handed over to the back-end */
+
+ pjob->spooled = True;
+ pjob->status = LPQ_QUEUED;
+ print_job_store(jobid, pjob);
+
+ /* make sure the database is up to date */
+ if (print_cache_expired(snum))
+ print_queue_update(snum);
+
+ return True;
+
+fail:
+
+ /* The print job was not succesfully started. Cleanup */
+ /* Still need to add proper error return propagation! 010122:JRR */
+ unlink(pjob->filename);
+ tdb_delete(tdb, print_key(jobid));
+ return False;
+}
+
+/* utility fn to enumerate the print queue */
+static int traverse_fn_queue(TDB_CONTEXT *t, TDB_DATA key, TDB_DATA data, void *state)
+{
+ struct traverse_struct *ts = (struct traverse_struct *)state;
+ struct printjob pjob;
+ int i, jobid;
+
+ if (data.dsize != sizeof(pjob) || key.dsize != sizeof(int)) return 0;
+ memcpy(&jobid, key.dptr, sizeof(jobid));
+ memcpy(&pjob, data.dptr, sizeof(pjob));
+
+ /* maybe it isn't for this queue */
+ if (ts->snum != lp_servicenumber(pjob.queuename))
+ return 0;
+
+ if (ts->qcount >= ts->maxcount) return 0;
+
+ i = ts->qcount;
+
+ 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->qcount++;
+
+ return 0;
+}
+
+struct traverse_count_struct {
+ int snum, count;
+};
+
+/* utility fn to count the number of entries in the print queue */
+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;
+ int jobid;
+
+ if (data.dsize != sizeof(pjob) || key.dsize != sizeof(int)) return 0;
+ memcpy(&jobid, key.dptr, sizeof(jobid));
+ memcpy(&pjob, data.dptr, sizeof(pjob));
+
+ /* maybe it isn't for this queue */
+ if (ts->snum != lp_servicenumber(pjob.queuename))
+ return 0;
+
+ ts->count++;
+
+ return 0;
+}
+
+/* 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;
+}
+
+/****************************************************************************
+get a printer queue listing
+****************************************************************************/
+int print_queue_status(int snum,
+ print_queue_struct **queue,
+ print_status_struct *status)
+{
+ struct traverse_struct tstruct;
+ struct traverse_count_struct tsc;
+ fstring keystr;
+ TDB_DATA data, key;
- /* only take the last part of the filename */
- {
- string tmp;
- char *p = strrchr(tok[6],'/');
- if (p)
- {
- strcpy(tmp,p+1);
- strcpy(tok[6],tmp);
- }
- }
+ /* make sure the database is up to date */
+ if (print_cache_expired(snum)) print_queue_update(snum);
+
+ *queue = NULL;
+
+ /*
+ * 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", lp_servicename(snum));
+ key.dptr = keystr;
+ key.dsize = strlen(keystr);
+ data = tdb_fetch(tdb, key);
+ if (data.dptr) {
+ if (data.dsize == sizeof(*status)) {
+ memcpy(status, data.dptr, sizeof(*status));
+ }
+ free(data.dptr);
+ }
+
+ /*
+ * 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(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)
+ 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(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(char *qname)
+{
+ int snum = lp_servicenumber(qname);
+ if (snum == -1 || !lp_print_ok(snum)) return -1;
+ return snum;
+}
+
+
+/****************************************************************************
+ pause a queue
+****************************************************************************/
+BOOL print_queue_pause(struct current_user *user, int snum, WERROR *errcode)
+{
+ char *printer_name;
+ int ret;
+
+ if (!print_access_check(user, snum, PRINTER_ACCESS_ADMINISTER)) {
+ *errcode = WERR_ACCESS_DENIED;
+ return False;
+ }
+
+ ret = (*(current_printif->queue_pause))(snum);
+
+ if (ret != 0) {
+ *errcode = WERR_INVALID_PARAM;
+ return False;
+ }
+
+ /* force update the database */
+ print_cache_flush(snum);
- buf->job = atoi(tok[2]);
- buf->size = atoi(tok[4]);
- buf->status = strequal(tok[3],"active")?LPQ_PRINTING:LPQ_QUEUED;
- buf->priority = 0;
- buf->time = time(NULL);
- StrnCpy(buf->user,tok[1],sizeof(buf->user)-1);
- StrnCpy(buf->file,tok[6],sizeof(buf->file)-1);
- return(True);
-}
-
-
-
-char *stat0_strings[] = { "enabled", "online", "idle", "no entries", "free", "ready", NULL };
-char *stat1_strings[] = { "offline", "disabled", "down", "off", "waiting", "no daemon", NULL };
-char *stat2_strings[] = { "jam", "paper", "error", "responding", "not accepting", "not running", "turned off", NULL };
-
-/****************************************************************************
-parse a lpq line. Choose printing style
-****************************************************************************/
-static BOOL parse_lpq_entry(int snum,char *line,
- print_queue_struct *buf,
- print_status_struct *status,BOOL first)
-{
- BOOL ret;
-
- switch (lp_printing())
- {
- case PRINT_SYSV:
- ret = parse_lpq_sysv(line,buf,first);
- break;
- case PRINT_AIX:
- ret = parse_lpq_aix(line,buf,first);
- break;
- case PRINT_HPUX:
- ret = parse_lpq_hpux(line,buf,first);
- break;
- case PRINT_QNX:
- ret = parse_lpq_qnx(line,buf,first);
- break;
- default:
- ret = parse_lpq_bsd(line,buf,first);
- break;
- }
-
-#ifdef LPQ_GUEST_TO_USER
- if (ret) {
- extern pstring sesssetup_user;
- /* change guest entries to the current logged in user to make
- them appear deletable to windows */
- if (sesssetup_user[0] && strequal(buf->user,lp_guestaccount(snum)))
- strcpy(buf->user,sesssetup_user);
- }
-#endif
-
- if (status && !ret)
- {
- /* a few simple checks to see if the line might be a
- printer status line:
- handle them so that most severe condition is shown */
- int i;
- strlower(line);
-
- switch (status->status) {
- case LPSTAT_OK:
- for (i=0; stat0_strings[i]; i++)
- if (strstr(line,stat0_strings[i])) {
- StrnCpy(status->message,line,sizeof(status->message)-1);
- status->status=LPSTAT_OK;
- }
- case LPSTAT_STOPPED:
- for (i=0; stat1_strings[i]; i++)
- if (strstr(line,stat1_strings[i])) {
- StrnCpy(status->message,line,sizeof(status->message)-1);
- status->status=LPSTAT_STOPPED;
- }
- case LPSTAT_ERROR:
- for (i=0; stat2_strings[i]; i++)
- if (strstr(line,stat2_strings[i])) {
- StrnCpy(status->message,line,sizeof(status->message)-1);
- status->status=LPSTAT_ERROR;
- }
- break;
- }
- }
-
- return(ret);
-}
-
-/****************************************************************************
-get a printer queue
-****************************************************************************/
-int get_printqueue(int snum,int cnum,print_queue_struct **queue,
- print_status_struct *status)
-{
- char *lpq_command = lp_lpqcommand(snum);
- char *printername = PRINTERNAME(snum);
- int ret=0,count=0;
- pstring syscmd;
- fstring outfile;
- pstring line;
- FILE *f;
- struct stat sbuf;
- BOOL dorun=True;
- int cachetime = lp_lpqcachetime();
- int lfd = -1;
-
- *line = 0;
- check_lpq_cache(snum);
-
- if (!printername || !*printername)
- {
- DEBUG(6,("replacing printer name with service (snum=(%s,%d))\n",
- lp_servicename(snum),snum));
- printername = lp_servicename(snum);
- }
-
- if (!lpq_command || !(*lpq_command))
- {
- DEBUG(5,("No lpq command\n"));
- return(0);
- }
-
- strcpy(syscmd,lpq_command);
- string_sub(syscmd,"%p",printername);
-
- standard_sub(cnum,syscmd);
-
- sprintf(outfile,"/tmp/lpq.%08x",str_checksum(syscmd));
-
- if (!lpq_cache_reset[snum] && cachetime && !stat(outfile,&sbuf))
- {
- if (time(NULL) - sbuf.st_mtime < cachetime) {
- DEBUG(3,("Using cached lpq output\n"));
- dorun = False;
- }
-
- if (dorun) {
- lfd = file_lock(outfile,LPQ_LOCK_TIMEOUT);
- if (lfd<0 ||
- (!fstat(lfd,&sbuf) && (time(NULL) - sbuf.st_mtime)<cachetime)) {
- DEBUG(3,("Using cached lpq output\n"));
- dorun = False;
- file_unlock(lfd); lfd = -1;
- }
- }
- }
-
- if (dorun) {
- ret = smbrun(syscmd,outfile);
- DEBUG(3,("Running the command `%s' gave %d\n",syscmd,ret));
- }
-
- lpq_cache_reset[snum] = False;
-
- f = fopen(outfile,"r");
- if (!f) {
- if (lfd >= 0) file_unlock(lfd);
- return(0);
- }
-
- if (status) {
- strcpy(status->message,"");
- status->status = LPSTAT_OK;
- }
-
- while (fgets(line,sizeof(pstring),f))
- {
- DEBUG(6,("QUEUE2: %s\n",line));
-
- *queue = Realloc(*queue,sizeof(print_queue_struct)*(count+1));
- if (! *queue)
- {
- count = 0;
- break;
- }
-
- bzero((char *)&(*queue)[count],sizeof(**queue));
-
- /* parse it */
- if (!parse_lpq_entry(snum,line,&(*queue)[count],status,count==0))
- continue;
-
- count++;
- }
-
- fclose(f);
-
- if (lfd >= 0) file_unlock(lfd);
-
- if (!cachetime)
- unlink(outfile);
- else
- chmod(outfile,0666);
- return(count);
-}
-
-
-/****************************************************************************
-delete a printer queue entry
-****************************************************************************/
-void del_printqueue(int cnum,int snum,int jobid)
-{
- char *lprm_command = lp_lprmcommand(snum);
- char *printername = PRINTERNAME(snum);
- pstring syscmd;
- char jobstr[20];
- int ret;
-
- if (!printername || !*printername)
- {
- DEBUG(6,("replacing printer name with service (snum=(%s,%d))\n",
- lp_servicename(snum),snum));
- printername = lp_servicename(snum);
- }
-
- if (!lprm_command || !(*lprm_command))
- {
- DEBUG(5,("No lprm command\n"));
- return;
- }
-
- sprintf(jobstr,"%d",jobid);
-
- strcpy(syscmd,lprm_command);
- string_sub(syscmd,"%p",printername);
- string_sub(syscmd,"%j",jobstr);
- standard_sub(cnum,syscmd);
-
- ret = smbrun(syscmd,NULL);
- DEBUG(3,("Running the command `%s' gave %d\n",syscmd,ret));
- lpq_reset(snum); /* queue has changed */
-}
-
-/****************************************************************************
-change status of a printer queue entry
-****************************************************************************/
-void status_printjob(int cnum,int snum,int jobid,int status)
-{
- char *lpstatus_command =
- (status==LPQ_PAUSED?lp_lppausecommand(snum):lp_lpresumecommand(snum));
- char *printername = PRINTERNAME(snum);
- pstring syscmd;
- char jobstr[20];
- int ret;
-
- if (!printername || !*printername)
- {
- DEBUG(6,("replacing printer name with service (snum=(%s,%d))\n",
- lp_servicename(snum),snum));
- printername = lp_servicename(snum);
- }
-
- if (!lpstatus_command || !(*lpstatus_command))
- {
- DEBUG(5,("No lpstatus command to %s job\n",
- (status==LPQ_PAUSED?"pause":"resume")));
- return;
- }
-
- sprintf(jobstr,"%d",jobid);
-
- strcpy(syscmd,lpstatus_command);
- string_sub(syscmd,"%p",printername);
- string_sub(syscmd,"%j",jobstr);
- standard_sub(cnum,syscmd);
+ /* Send a printer notify message */
- ret = smbrun(syscmd,NULL);
- DEBUG(3,("Running the command `%s' gave %d\n",syscmd,ret));
- lpq_reset(snum); /* queue has changed */
+ printer_name = PRINTERNAME(snum);
+
+ send_queue_message(printer_name, 0, PRINTER_CHANGE_JOB);
+
+ return True;
}
+/****************************************************************************
+ resume a queue
+****************************************************************************/
+BOOL print_queue_resume(struct current_user *user, int snum, WERROR *errcode)
+{
+ char *printer_name;
+ int ret;
+
+ if (!print_access_check(user, snum, PRINTER_ACCESS_ADMINISTER)) {
+ *errcode = WERR_ACCESS_DENIED;
+ return False;
+ }
+
+ ret = (*(current_printif->queue_resume))(snum);
+ if (ret != 0) {
+ *errcode = WERR_INVALID_PARAM;
+ return False;
+ }
+
+ /* make sure the database is up to date */
+ if (print_cache_expired(snum)) print_queue_update(snum);
+
+ /* Send a printer notify message */
+
+ printer_name = PRINTERNAME(snum);
+
+ send_queue_message(printer_name, 0, PRINTER_CHANGE_JOB);
+
+ return True;
+}
+
+/****************************************************************************
+ purge a queue - implemented by deleting all jobs that we can delete
+****************************************************************************/
+BOOL print_queue_purge(struct current_user *user, int snum, WERROR *errcode)
+{
+ print_queue_struct *queue;
+ print_status_struct status;
+ char *printer_name;
+ int njobs, i;
+ BOOL can_job_admin;
+
+ /* Force and update so the count is accurate (i.e. not a cached count) */
+ print_queue_update(snum);
+
+ can_job_admin = print_access_check(user, snum, JOB_ACCESS_ADMINISTER);
+ njobs = print_queue_status(snum, &queue, &status);
+
+ for (i=0;i<njobs;i++) {
+ BOOL owner = is_owner(user, queue[i].job);
+
+ if (owner || can_job_admin) {
+ print_job_delete1(queue[i].job);
+ }
+ }
+
+ safe_free(queue);
+
+ /* Send a printer notify message */
+
+ printer_name = PRINTERNAME(snum);
+
+ send_queue_message(printer_name, 0, PRINTER_CHANGE_JOB);
+
+ return True;
+}