s3-printing: fix potential print db refcount leak
[ddiss/samba.git] / source3 / printing / printing.c
index 31b802d3fbda4effcc3cb35b7ce691f0e9379a65..f928557c02f87f051c7180aeea3ff2f9d03d492e 100644 (file)
 */
 
 #include "includes.h"
+#include "system/syslog.h"
+#include "system/filesys.h"
 #include "printing.h"
-#include "librpc/gen_ndr/messaging.h"
 #include "../librpc/gen_ndr/ndr_spoolss.h"
 #include "nt_printing.h"
 #include "../librpc/gen_ndr/netlogon.h"
+#include "printing/notify.h"
+#include "printing/pcap.h"
+#include "serverid.h"
+#include "smbd/smbd.h"
+#include "auth.h"
+#include "messages.h"
+#include "util_tdb.h"
 
 extern struct current_user current_user;
 extern userdom_struct current_user_info;
 
 /* Current printer interface */
-static bool remove_from_jobs_changed(const char* sharename, uint32 jobid);
+static bool remove_from_jobs_added(const char* sharename, uint32 jobid);
 
 /*
    the printing backend revolves around a tdb database that stores the
@@ -365,18 +373,20 @@ done:
  unpack a pjob from a tdb buffer
 ***********************************************************************/
 
-int unpack_pjob( uint8 *buf, int buflen, struct printjob *pjob )
+static int unpack_pjob(uint8 *buf, int buflen, struct printjob *pjob)
 {
        int     len = 0;
        int     used;
-       uint32 pjpid, pjsysjob, pjfd, pjstarttime, pjstatus;
+       uint32 pjpid, pjjobid, pjsysjob, pjfd, pjstarttime, pjstatus;
        uint32 pjsize, pjpage_count, pjspooled, pjsmbjob;
 
-       if ( !buf || !pjob )
+       if (!buf || !pjob) {
                return -1;
+       }
 
-       len += tdb_unpack(buf+len, buflen-len, "dddddddddfffff",
+       len += tdb_unpack(buf+len, buflen-len, "ddddddddddfffff",
                                &pjpid,
+                               &pjjobid,
                                &pjsysjob,
                                &pjfd,
                                &pjstarttime,
@@ -391,8 +401,9 @@ int unpack_pjob( uint8 *buf, int buflen, struct printjob *pjob )
                                pjob->clientmachine,
                                pjob->queuename);
 
-       if ( len == -1 )
+       if (len == -1) {
                return -1;
+       }
 
         used = unpack_devicemode(NULL, buf+len, buflen-len, &pjob->devmode);
         if (used == -1) {
@@ -402,6 +413,7 @@ int unpack_pjob( uint8 *buf, int buflen, struct printjob *pjob )
        len += used;
 
        pjob->pid = pjpid;
+       pjob->jobid = pjjobid;
        pjob->sysjob = pjsysjob;
        pjob->fd = pjfd;
        pjob->starttime = pjstarttime;
@@ -455,6 +467,7 @@ static struct printjob *print_job_find(const char *sharename, uint32 jobid)
 
        DEBUG(10,("print_job_find: returning system job %d for jobid %u.\n",
                        (int)pjob.sysjob, (unsigned int)jobid ));
+       SMB_ASSERT(pjob.jobid == jobid);
 
        return &pjob;
 }
@@ -481,15 +494,25 @@ static int unixjob_traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA key,
                return 0;
 
        if (state->sysjob == pjob->sysjob) {
-               uint32 jobid = IVAL(key.dptr,0);
-
-               state->sysjob_to_jobid_value = jobid;
+               state->sysjob_to_jobid_value = pjob->jobid;
                return 1;
        }
 
        return 0;
 }
 
+static uint32 sysjob_to_jobid_pdb(struct tdb_print_db *pdb, int sysjob)
+{
+       struct unixjob_traverse_state state;
+
+       state.sysjob = sysjob;
+       state.sysjob_to_jobid_value = (uint32)-1;
+
+       tdb_traverse(pdb->tdb, unixjob_traverse_fn, &state);
+
+       return state.sysjob_to_jobid_value;
+}
+
 /****************************************************************************
  This is a *horribly expensive call as we have to iterate through all the
  current printer tdb's. Don't do this often ! JRA.
@@ -525,8 +548,8 @@ uint32 sysjob_to_jobid(int unix_jobid)
 ****************************************************************************/
 
 static const struct {
-       uint32 lpq_status;
-       uint32 spoolss_status;
+       uint32_t lpq_status;
+       uint32_t spoolss_status;
 } lpq_to_spoolss_status_map[] = {
        { LPQ_QUEUED, JOB_STATUS_QUEUED },
        { LPQ_PAUSED, JOB_STATUS_PAUSED },
@@ -539,7 +562,7 @@ static const struct {
        { LPQ_DELETED, JOB_STATUS_DELETED },
        { LPQ_BLOCKED, JOB_STATUS_BLOCKED_DEVQ },
        { LPQ_USER_INTERVENTION, JOB_STATUS_USER_INTERVENTION },
-       { -1, 0 }
+       { (uint32_t)-1, 0 }
 };
 
 /* Convert a lpq status value stored in printing.tdb into the
@@ -558,19 +581,97 @@ static uint32 map_to_spoolss_status(uint32 lpq_status)
        return 0;
 }
 
+/***************************************************************************
+ Append a jobid to the 'jobs changed' list.
+***************************************************************************/
+
+static bool add_to_jobs_changed(struct tdb_print_db *pdb, uint32_t jobid)
+{
+       TDB_DATA data;
+       uint32_t store_jobid;
+
+       SIVAL(&store_jobid, 0, jobid);
+       data.dptr = (uint8 *) &store_jobid;
+       data.dsize = 4;
+
+       DEBUG(10,("add_to_jobs_added: Added jobid %u\n", (unsigned int)jobid ));
+
+       return (tdb_append(pdb->tdb, string_tdb_data("INFO/jobs_changed"),
+                          data) == 0);
+}
+
+/***************************************************************************
+ Remove a jobid from the 'jobs changed' list.
+***************************************************************************/
+
+static bool remove_from_jobs_changed(const char* sharename, uint32_t jobid)
+{
+       struct tdb_print_db *pdb = get_print_db_byname(sharename);
+       TDB_DATA data, key;
+       size_t job_count, i;
+       bool ret = False;
+       bool gotlock = False;
+
+       if (!pdb) {
+               return False;
+       }
+
+       ZERO_STRUCT(data);
+
+       key = string_tdb_data("INFO/jobs_changed");
+
+       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;
+
+               ch_jobid = IVAL(data.dptr, i*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;
+}
+
 static void pjob_store_notify(struct tevent_context *ev,
                              struct messaging_context *msg_ctx,
                              const char* sharename, uint32 jobid,
                              struct printjob *old_data,
-                             struct printjob *new_data)
+                             struct printjob *new_data,
+                             bool *pchanged)
 {
-       bool new_job = False;
-
-       if (!old_data)
-               new_job = True;
+       bool new_job = false;
+       bool changed = false;
 
-       /* Job attributes that can't be changed.  We only send
-          notification for these on a new job. */
+       if (old_data == NULL) {
+               new_job = true;
+       }
 
        /* ACHTUNG!  Due to a bug in Samba's spoolss parsing of the
           NOTIFY_INFO_DATA buffer, we *have* to send the job submission
@@ -584,31 +685,40 @@ static void pjob_store_notify(struct tevent_context *ev,
                                     sharename, jobid, new_data->starttime);
                notify_job_username(ev, msg_ctx,
                                    sharename, jobid, new_data->user);
-       }
-
-       if (new_job || !strequal(old_data->jobname, new_data->jobname))
-               notify_job_name(ev, msg_ctx,
-                               sharename, jobid, new_data->jobname);
-
-       /* Job attributes of a new job or attributes that can be
-          modified. */
-
-       if (new_job || !strequal(old_data->jobname, new_data->jobname))
                notify_job_name(ev, msg_ctx,
                                sharename, jobid, new_data->jobname);
-
-       if (new_job || old_data->status != new_data->status)
                notify_job_status(ev, msg_ctx,
-                                 sharename, jobid,
-                                 map_to_spoolss_status(new_data->status));
-
-       if (new_job || old_data->size != new_data->size)
+                                 sharename, jobid, map_to_spoolss_status(new_data->status));
                notify_job_total_bytes(ev, msg_ctx,
                                       sharename, jobid, new_data->size);
-
-       if (new_job || old_data->page_count != new_data->page_count)
                notify_job_total_pages(ev, msg_ctx,
                                       sharename, jobid, new_data->page_count);
+       } else {
+               if (!strequal(old_data->jobname, new_data->jobname)) {
+                       notify_job_name(ev, msg_ctx, sharename,
+                                       jobid, new_data->jobname);
+                       changed = true;
+               }
+
+               if (old_data->status != new_data->status) {
+                       notify_job_status(ev, msg_ctx,
+                                         sharename, jobid,
+                                         map_to_spoolss_status(new_data->status));
+               }
+
+               if (old_data->size != new_data->size) {
+                       notify_job_total_bytes(ev, msg_ctx,
+                                              sharename, jobid, new_data->size);
+               }
+
+               if (old_data->page_count != new_data->page_count) {
+                       notify_job_total_pages(ev, msg_ctx,
+                                              sharename, jobid,
+                                              new_data->page_count);
+               }
+       }
+
+       *pchanged = changed;
 }
 
 /****************************************************************************
@@ -642,8 +752,9 @@ static bool pjob_store(struct tevent_context *ev,
        do {
                len = 0;
                buflen = newlen;
-               len += tdb_pack(buf+len, buflen-len, "dddddddddfffff",
+               len += tdb_pack(buf+len, buflen-len, "ddddddddddfffff",
                                (uint32)pjob->pid,
+                               (uint32)pjob->jobid,
                                (uint32)pjob->sysjob,
                                (uint32)pjob->fd,
                                (uint32)pjob->starttime,
@@ -678,11 +789,10 @@ static bool pjob_store(struct tevent_context *ev,
        ret = (tdb_store(pdb->tdb, print_key(jobid, &tmp), new_data,
                         TDB_REPLACE) == 0);
 
-       release_print_db(pdb);
-
        /* Send notify updates for what has changed */
 
        if ( ret ) {
+               bool changed = false;
                struct printjob old_pjob;
 
                if ( old_data.dsize )
@@ -692,18 +802,26 @@ static bool pjob_store(struct tevent_context *ev,
                                pjob_store_notify(server_event_context(),
                                                  msg_ctx,
                                                  sharename, jobid, &old_pjob,
-                                                 pjob);
+                                                 pjob,
+                                                 &changed);
                                talloc_free(old_pjob.devmode);
+
+                               if (changed) {
+                                       add_to_jobs_changed(pdb, jobid);
+                               }
                        }
+
                }
                else {
                        /* new job */
                        pjob_store_notify(server_event_context(), msg_ctx,
-                                         sharename, jobid, NULL, pjob);
+                                         sharename, jobid, NULL, pjob,
+                                         &changed);
                }
        }
 
 done:
+       release_print_db(pdb);
        SAFE_FREE( old_data.dptr );
        SAFE_FREE( buf );
 
@@ -747,7 +865,7 @@ static void pjob_delete(struct tevent_context *ev,
        /* Remove from printing.tdb */
 
        tdb_delete(pdb->tdb, print_key(jobid, &tmp));
-       remove_from_jobs_changed(sharename, jobid);
+       remove_from_jobs_added(sharename, jobid);
        release_print_db( pdb );
        rap_jobid_delete(sharename, jobid);
 }
@@ -764,7 +882,7 @@ static void print_unix_job(struct tevent_context *ev,
        struct printjob pj, *old_pj;
 
        if (jobid == (uint32)-1)
-               jobid = q->job + UNIX_JOB_START;
+               jobid = q->sysjob + UNIX_JOB_START;
 
        /* Preserve the timestamp on an existing unix print job */
 
@@ -773,7 +891,8 @@ static void print_unix_job(struct tevent_context *ev,
        ZERO_STRUCT(pj);
 
        pj.pid = (pid_t)-1;
-       pj.sysjob = q->job;
+       pj.jobid = jobid;
+       pj.sysjob = q->sysjob;
        pj.fd = -1;
        pj.starttime = old_pj ? old_pj->starttime : q->time;
        pj.status = q->status;
@@ -819,19 +938,17 @@ static int traverse_fn_delete(TDB_CONTEXT *t, TDB_DATA key, TDB_DATA data, void
        if (  key.dsize != sizeof(jobid) )
                return 0;
 
-       jobid = IVAL(key.dptr, 0);
-       if ( unpack_pjob( data.dptr, data.dsize, &pjob ) == -1 )
+       if (unpack_pjob(data.dptr, data.dsize, &pjob) == -1)
                return 0;
        talloc_free(pjob.devmode);
-
+       jobid = pjob.jobid;
 
        if (!pjob.smbjob) {
                /* remove a unix job if it isn't in the system queue any more */
-
                for (i=0;i<ts->qcount;i++) {
-                       uint32 u_jobid = (ts->queue[i].job + UNIX_JOB_START);
-                       if (jobid == u_jobid)
+                       if (ts->queue[i].sysjob == pjob.sysjob) {
                                break;
+                       }
                }
                if (i == ts->qcount) {
                        DEBUG(10,("traverse_fn_delete: pjob %u deleted due to !smbjob\n",
@@ -862,16 +979,12 @@ static int traverse_fn_delete(TDB_CONTEXT *t, TDB_DATA key, TDB_DATA data, void
 
        /* this check only makes sense for jobs submitted from Windows clients */
 
-       if ( pjob.smbjob ) {
+       if (pjob.smbjob) {
                for (i=0;i<ts->qcount;i++) {
-                       uint32 curr_jobid;
-
                        if ( pjob.status == LPQ_DELETED )
                                continue;
 
-                       curr_jobid = print_parse_jobid(ts->queue[i].fs_file);
-
-                       if (jobid == curr_jobid) {
+                       if (ts->queue[i].sysjob == pjob.sysjob) {
 
                                /* try to clean up any jobs that need to be deleted */
 
@@ -926,11 +1039,8 @@ static int traverse_fn_delete(TDB_CONTEXT *t, TDB_DATA key, TDB_DATA data, void
                return 0;
        }
 
-       /* Save the pjob attributes we will store.
-          FIXME!!! This is the only place where queue->job
-          represents the SMB jobid      --jerry */
-
-       ts->queue[i].job = jobid;
+       /* Save the pjob attributes we will store. */
+       ts->queue[i].sysjob = pjob.sysjob;
        ts->queue[i].size = pjob.size;
        ts->queue[i].page_count = pjob.page_count;
        ts->queue[i].status = pjob.status;
@@ -1081,7 +1191,7 @@ static void store_queue_struct(struct tdb_print_db *pdb, struct traverse_struct
 
                qcount++;
                data.dsize += tdb_pack(NULL, 0, "ddddddff",
-                               (uint32)queue[i].job,
+                               (uint32)queue[i].sysjob,
                                (uint32)queue[i].size,
                                (uint32)queue[i].page_count,
                                (uint32)queue[i].status,
@@ -1101,7 +1211,7 @@ static void store_queue_struct(struct tdb_print_db *pdb, struct traverse_struct
                        continue;
 
                len += tdb_pack(data.dptr + len, data.dsize - len, "ddddddff",
-                               (uint32)queue[i].job,
+                               (uint32)queue[i].sysjob,
                                (uint32)queue[i].size,
                                (uint32)queue[i].page_count,
                                (uint32)queue[i].status,
@@ -1117,13 +1227,13 @@ static void store_queue_struct(struct tdb_print_db *pdb, struct traverse_struct
        return;
 }
 
-static TDB_DATA get_jobs_changed_data(struct tdb_print_db *pdb)
+static TDB_DATA get_jobs_added_data(struct tdb_print_db *pdb)
 {
        TDB_DATA data;
 
        ZERO_STRUCT(data);
 
-       data = tdb_fetch(pdb->tdb, string_tdb_data("INFO/jobs_changed"));
+       data = tdb_fetch(pdb->tdb, string_tdb_data("INFO/jobs_added"));
        if (data.dptr == NULL || data.dsize == 0 || (data.dsize % 4 != 0)) {
                SAFE_FREE(data.dptr);
                ZERO_STRUCT(data);
@@ -1132,7 +1242,7 @@ static TDB_DATA get_jobs_changed_data(struct tdb_print_db *pdb)
        return data;
 }
 
-static void check_job_changed(const char *sharename, TDB_DATA data, uint32 jobid)
+static void check_job_added(const char *sharename, TDB_DATA data, uint32 jobid)
 {
        unsigned int i;
        unsigned int job_count = data.dsize / 4;
@@ -1142,7 +1252,7 @@ static void check_job_changed(const char *sharename, TDB_DATA data, uint32 jobid
 
                ch_jobid = IVAL(data.dptr, i*4);
                if (ch_jobid == jobid)
-                       remove_from_jobs_changed(sharename, jobid);
+                       remove_from_jobs_added(sharename, jobid);
        }
 }
 
@@ -1212,14 +1322,14 @@ done:
 }
 
 /****************************************************************************
- main work for updating the lpq cahe for a printer queue
+ main work for updating the lpq cache for a printer queue
 ****************************************************************************/
 
-static void print_queue_update_internal( struct tevent_context *ev,
-                                        struct messaging_context *msg_ctx,
-                                        const char *sharename,
-                                         struct printif *current_printif,
-                                         char *lpq_command, char *lprm_command )
+static void print_queue_update_internal(struct tevent_context *ev,
+                                       struct messaging_context *msg_ctx,
+                                       const char *sharename,
+                                        struct printif *current_printif,
+                                        char *lpq_command, char *lprm_command)
 {
        int i, qcount;
        print_queue_struct *queue = NULL;
@@ -1274,11 +1384,10 @@ static void print_queue_update_internal( struct tevent_context *ev,
          fill in any system job numbers as we go
        */
 
-       jcdata = get_jobs_changed_data(pdb);
+       jcdata = get_jobs_added_data(pdb);
 
        for (i=0; i<qcount; i++) {
-               uint32 jobid = print_parse_jobid(queue[i].fs_file);
-
+               uint32 jobid = sysjob_to_jobid_pdb(pdb, queue[i].sysjob);
                if (jobid == (uint32)-1) {
                        /* assume its a unix print job */
                        print_unix_job(ev, msg_ctx,
@@ -1292,13 +1401,13 @@ static void print_queue_update_internal( struct tevent_context *ev,
                        /* err, somethings wrong. Probably smbd was restarted
                           with jobs in the queue. All we can do is treat them
                           like unix jobs. Pity. */
+                       DEBUG(1, ("queued print job %d not found in jobs list, "
+                                 "assuming unix job\n", jobid));
                        print_unix_job(ev, msg_ctx,
                                       sharename, &queue[i], jobid);
                        continue;
                }
 
-               pjob->sysjob = queue[i].job;
-
                /* don't reset the status on jobs to be deleted */
 
                if ( pjob->status != LPQ_DELETING )
@@ -1306,7 +1415,7 @@ static void print_queue_update_internal( struct tevent_context *ev,
 
                pjob_store(ev, msg_ctx, sharename, jobid, pjob);
 
-               check_job_changed(sharename, jcdata, jobid);
+               check_job_added(sharename, jcdata, jobid);
        }
 
        SAFE_FREE(jcdata.dptr);
@@ -1463,7 +1572,7 @@ static void print_queue_update_with_lock( struct tevent_context *ev,
 /****************************************************************************
 this is the receive function of the background lpq updater
 ****************************************************************************/
-static void print_queue_receive(struct messaging_context *msg,
+void print_queue_receive(struct messaging_context *msg,
                                void *private_data,
                                uint32_t msg_type,
                                struct server_id server_id,
@@ -2013,10 +2122,10 @@ bool print_job_get_name(TALLOC_CTX *mem_ctx, const char *sharename, uint32_t job
 
 
 /***************************************************************************
- Remove a jobid from the 'jobs changed' list.
+ Remove a jobid from the 'jobs added' list.
 ***************************************************************************/
 
-static bool remove_from_jobs_changed(const char* sharename, uint32 jobid)
+static bool remove_from_jobs_added(const char* sharename, uint32 jobid)
 {
        struct tdb_print_db *pdb = get_print_db_byname(sharename);
        TDB_DATA data, key;
@@ -2030,7 +2139,7 @@ static bool remove_from_jobs_changed(const char* sharename, uint32 jobid)
 
        ZERO_STRUCT(data);
 
-       key = string_tdb_data("INFO/jobs_changed");
+       key = string_tdb_data("INFO/jobs_added");
 
        if (tdb_chainlock_with_timeout(pdb->tdb, key, 5) == -1)
                goto out;
@@ -2065,9 +2174,9 @@ static bool remove_from_jobs_changed(const char* sharename, uint32 jobid)
        SAFE_FREE(data.dptr);
        release_print_db(pdb);
        if (ret)
-               DEBUG(10,("remove_from_jobs_changed: removed jobid %u\n", (unsigned int)jobid ));
+               DEBUG(10,("remove_from_jobs_added: removed jobid %u\n", (unsigned int)jobid ));
        else
-               DEBUG(10,("remove_from_jobs_changed: Failed to remove jobid %u\n", (unsigned int)jobid ));
+               DEBUG(10,("remove_from_jobs_added: Failed to remove jobid %u\n", (unsigned int)jobid ));
        return ret;
 }
 
@@ -2131,7 +2240,7 @@ static bool print_job_delete1(struct tevent_context *ev,
                }
        }
 
-       remove_from_jobs_changed( sharename, jobid );
+       remove_from_jobs_added( sharename, jobid );
 
        return (result == 0);
 }
@@ -2140,7 +2249,7 @@ static bool print_job_delete1(struct tevent_context *ev,
  Return true if the current user owns the print job.
 ****************************************************************************/
 
-static bool is_owner(struct auth_serversupplied_info *server_info,
+static bool is_owner(const struct auth_serversupplied_info *server_info,
                     const char *servicename,
                     uint32 jobid)
 {
@@ -2156,7 +2265,7 @@ static bool is_owner(struct auth_serversupplied_info *server_info,
  Delete a print job.
 ****************************************************************************/
 
-WERROR print_job_delete(struct auth_serversupplied_info *server_info,
+WERROR print_job_delete(const struct auth_serversupplied_info *server_info,
                        struct messaging_context *msg_ctx,
                        int snum, uint32_t jobid)
 {
@@ -2224,7 +2333,7 @@ pause, or resume print job. User name: %s. Printer name: %s.",
  Pause a job.
 ****************************************************************************/
 
-bool print_job_pause(struct auth_serversupplied_info *server_info,
+bool print_job_pause(const struct auth_serversupplied_info *server_info,
                     struct messaging_context *msg_ctx,
                     int snum, uint32 jobid, WERROR *errcode)
 {
@@ -2289,7 +2398,7 @@ pause, or resume print job. User name: %s. Printer name: %s.",
  Resume a job.
 ****************************************************************************/
 
-bool print_job_resume(struct auth_serversupplied_info *server_info,
+bool print_job_resume(const struct auth_serversupplied_info *server_info,
                      struct messaging_context *msg_ctx,
                      int snum, uint32 jobid, WERROR *errcode)
 {
@@ -2352,8 +2461,7 @@ pause, or resume print job. User name: %s. Printer name: %s.",
 
 ssize_t print_job_write(struct tevent_context *ev,
                        struct messaging_context *msg_ctx,
-                       int snum, uint32 jobid, const char *buf, SMB_OFF_T pos,
-                       size_t size)
+                       int snum, uint32 jobid, const char *buf, size_t size)
 {
        const char* sharename = lp_const_servicename(snum);
        ssize_t return_code;
@@ -2372,7 +2480,7 @@ ssize_t print_job_write(struct tevent_context *ev,
                return -1;
        }
 
-       return_code = write_data_at_offset(pjob->fd, buf, size, pos);
+       return_code = write_data(pjob->fd, buf, size);
 
        if (return_code>0) {
                pjob->size += size;
@@ -2535,10 +2643,10 @@ static WERROR allocate_print_jobid(struct tdb_print_db *pdb, int snum,
 }
 
 /***************************************************************************
- Append a jobid to the 'jobs changed' list.
+ Append a jobid to the 'jobs added' list.
 ***************************************************************************/
 
-static bool add_to_jobs_changed(struct tdb_print_db *pdb, uint32 jobid)
+static bool add_to_jobs_added(struct tdb_print_db *pdb, uint32 jobid)
 {
        TDB_DATA data;
        uint32 store_jobid;
@@ -2547,9 +2655,9 @@ static bool add_to_jobs_changed(struct tdb_print_db *pdb, uint32 jobid)
        data.dptr = (uint8 *)&store_jobid;
        data.dsize = 4;
 
-       DEBUG(10,("add_to_jobs_changed: Added jobid %u\n", (unsigned int)jobid ));
+       DEBUG(10,("add_to_jobs_added: Added jobid %u\n", (unsigned int)jobid ));
 
-       return (tdb_append(pdb->tdb, string_tdb_data("INFO/jobs_changed"),
+       return (tdb_append(pdb->tdb, string_tdb_data("INFO/jobs_added"),
                           data) == 0);
 }
 
@@ -2558,7 +2666,7 @@ static bool add_to_jobs_changed(struct tdb_print_db *pdb, uint32 jobid)
  Do all checks needed to determine if we can start a job.
 ***************************************************************************/
 
-static WERROR print_job_checks(struct auth_serversupplied_info *server_info,
+static WERROR print_job_checks(const struct auth_serversupplied_info *server_info,
                               struct messaging_context *msg_ctx,
                               int snum, int *njobs)
 {
@@ -2684,8 +2792,9 @@ static WERROR print_job_spool_file(int snum, uint32_t jobid,
  Start spooling a job - return the jobid.
 ***************************************************************************/
 
-WERROR print_job_start(struct auth_serversupplied_info *server_info,
+WERROR print_job_start(const struct auth_serversupplied_info *server_info,
                       struct messaging_context *msg_ctx,
+                      const char *clientmachine,
                       int snum, const char *docname, const char *filename,
                       struct spoolss_DeviceMode *devmode, uint32_t *_jobid)
 {
@@ -2723,6 +2832,7 @@ WERROR print_job_start(struct auth_serversupplied_info *server_info,
        ZERO_STRUCT(pjob);
 
        pjob.pid = sys_getpid();
+       pjob.jobid = jobid;
        pjob.sysjob = -1;
        pjob.fd = -1;
        pjob.starttime = time(NULL);
@@ -2734,6 +2844,8 @@ WERROR print_job_start(struct auth_serversupplied_info *server_info,
 
        fstrcpy(pjob.jobname, docname);
 
+       fstrcpy(pjob.clientmachine, clientmachine);
+
        fstrcpy(pjob.user, lp_printjob_username(snum));
        standard_sub_advanced(sharename, server_info->sanitized_username,
                              path, server_info->utok.gid,
@@ -2753,8 +2865,8 @@ WERROR print_job_start(struct auth_serversupplied_info *server_info,
 
        pjob_store(server_event_context(), msg_ctx, sharename, jobid, &pjob);
 
-       /* Update the 'jobs changed' entry used by print_queue_status. */
-       add_to_jobs_changed(pdb, jobid);
+       /* Update the 'jobs added' entry used by print_queue_status. */
+       add_to_jobs_added(pdb, jobid);
 
        /* Ensure we keep a rough count of the number of total jobs... */
        tdb_change_int32_atomic(pdb->tdb, "INFO/total_jobs", &njobs, 1);
@@ -2812,8 +2924,6 @@ NTSTATUS print_job_end(struct messaging_context *msg_ctx, int snum,
        SMB_STRUCT_STAT sbuf;
        struct printif *current_printif = get_printer_fns( snum );
        NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
-       const char *clientname;
-       char addr[INET6_ADDRSTRLEN];
 
        pjob = print_job_find(sharename, jobid);
 
@@ -2876,12 +2986,6 @@ NTSTATUS print_job_end(struct messaging_context *msg_ctx, int snum,
                return NT_STATUS_OK;
        }
 
-       clientname = client_name(smbd_server_fd());
-       if (strcmp(clientname, "UNKNOWN") == 0) {
-               clientname = client_addr(smbd_server_fd(),addr,sizeof(addr));
-       }
-       fstrcpy(pjob->clientmachine, clientname);
-
        ret = (*(current_printif->job_submit))(snum, pjob);
 
        if (ret) {
@@ -2919,10 +3023,11 @@ static bool get_stored_queue_info(struct messaging_context *msg_ctx,
                                  struct tdb_print_db *pdb, int snum,
                                  int *pcount, print_queue_struct **ppqueue)
 {
-       TDB_DATA data, cgdata;
+       TDB_DATA data, cgdata, jcdata;
        print_queue_struct *queue = NULL;
        uint32 qcount = 0;
        uint32 extra_count = 0;
+       uint32_t changed_count = 0;
        int total_count = 0;
        size_t len = 0;
        uint32 i;
@@ -2946,11 +3051,16 @@ static bool get_stored_queue_info(struct messaging_context *msg_ctx,
        if (data.dptr && data.dsize >= sizeof(qcount))
                len += tdb_unpack(data.dptr + len, data.dsize - len, "d", &qcount);
 
-       /* Get the changed jobs list. */
-       cgdata = tdb_fetch(pdb->tdb, string_tdb_data("INFO/jobs_changed"));
+       /* Get the added jobs list. */
+       cgdata = tdb_fetch(pdb->tdb, string_tdb_data("INFO/jobs_added"));
        if (cgdata.dptr != NULL && (cgdata.dsize % 4 == 0))
                extra_count = cgdata.dsize/4;
 
+       /* Get the changed jobs list. */
+       jcdata = tdb_fetch(pdb->tdb, string_tdb_data("INFO/jobs_changed"));
+       if (jcdata.dptr != NULL && (jcdata.dsize % 4 == 0))
+               changed_count = jcdata.dsize / 4;
+
        DEBUG(5,("get_stored_queue_info: qcount = %u, extra_count = %u\n", (unsigned int)qcount, (unsigned int)extra_count));
 
        /* Allocate the queue size. */
@@ -2973,7 +3083,7 @@ static bool get_stored_queue_info(struct messaging_context *msg_ctx,
                                &qtime,
                                queue[i].fs_user,
                                queue[i].fs_file);
-               queue[i].job = qjob;
+               queue[i].sysjob = qjob;
                queue[i].size = qsize;
                queue[i].page_count = qpage_count;
                queue[i].status = qstatus;
@@ -2983,21 +3093,21 @@ static bool get_stored_queue_info(struct messaging_context *msg_ctx,
 
        total_count = qcount;
 
-       /* Add in the changed jobids. */
+       /* Add new jobids to the queue. */
        for( i  = 0; i < extra_count; i++) {
                uint32 jobid;
                struct printjob *pjob;
 
                jobid = IVAL(cgdata.dptr, i*4);
-               DEBUG(5,("get_stored_queue_info: changed job = %u\n", (unsigned int)jobid));
+               DEBUG(5,("get_stored_queue_info: added job = %u\n", (unsigned int)jobid));
                pjob = print_job_find(lp_const_servicename(snum), jobid);
                if (!pjob) {
-                       DEBUG(5,("get_stored_queue_info: failed to find changed job = %u\n", (unsigned int)jobid));
-                       remove_from_jobs_changed(sharename, jobid);
+                       DEBUG(5,("get_stored_queue_info: failed to find added job = %u\n", (unsigned int)jobid));
+                       remove_from_jobs_added(sharename, jobid);
                        continue;
                }
 
-               queue[total_count].job = jobid;
+               queue[total_count].sysjob = jobid;
                queue[total_count].size = pjob->size;
                queue[total_count].page_count = pjob->page_count;
                queue[total_count].status = pjob->status;
@@ -3008,6 +3118,50 @@ static bool get_stored_queue_info(struct messaging_context *msg_ctx,
                total_count++;
        }
 
+       /* Update the changed jobids. */
+       for (i = 0; i < changed_count; i++) {
+               uint32_t jobid = IVAL(jcdata.dptr, i * 4);
+               uint32_t j;
+               bool found = false;
+
+               for (j = 0; j < total_count; j++) {
+                       if (queue[j].sysjob == jobid) {
+                               found = true;
+                               break;
+                       }
+               }
+
+               if (found) {
+                       struct printjob *pjob;
+
+                       DEBUG(5,("get_stored_queue_info: changed job: %u\n",
+                                (unsigned int) jobid));
+
+                       pjob = print_job_find(sharename, jobid);
+                       if (pjob == NULL) {
+                               DEBUG(5,("get_stored_queue_info: failed to find "
+                                        "changed job = %u\n",
+                                        (unsigned int) jobid));
+                               remove_from_jobs_changed(sharename, jobid);
+                               continue;
+                       }
+
+                       queue[j].sysjob = jobid;
+                       queue[j].size = pjob->size;
+                       queue[j].page_count = pjob->page_count;
+                       queue[j].status = pjob->status;
+                       queue[j].priority = 1;
+                       queue[j].time = pjob->starttime;
+                       fstrcpy(queue[j].fs_user, pjob->user);
+                       fstrcpy(queue[j].fs_file, pjob->jobname);
+
+                       DEBUG(5,("get_stored_queue_info: updated queue[%u], jobid: %u, jobname: %s\n",
+                                (unsigned int) j, (unsigned int) jobid, pjob->jobname));
+               }
+
+               remove_from_jobs_changed(sharename, jobid);
+       }
+
        /* Sort the queue by submission time otherwise they are displayed
           in hash order. */
 
@@ -3098,7 +3252,7 @@ int print_queue_status(struct messaging_context *msg_ctx, int snum,
  Pause a queue.
 ****************************************************************************/
 
-WERROR print_queue_pause(struct auth_serversupplied_info *server_info,
+WERROR print_queue_pause(const struct auth_serversupplied_info *server_info,
                         struct messaging_context *msg_ctx, int snum)
 {
        int ret;
@@ -3135,7 +3289,7 @@ WERROR print_queue_pause(struct auth_serversupplied_info *server_info,
  Resume a queue.
 ****************************************************************************/
 
-WERROR print_queue_resume(struct auth_serversupplied_info *server_info,
+WERROR print_queue_resume(const struct auth_serversupplied_info *server_info,
                          struct messaging_context *msg_ctx, int snum)
 {
        int ret;
@@ -3172,7 +3326,7 @@ WERROR print_queue_resume(struct auth_serversupplied_info *server_info,
  Purge a queue - implemented by deleting all jobs that we can delete.
 ****************************************************************************/
 
-WERROR print_queue_purge(struct auth_serversupplied_info *server_info,
+WERROR print_queue_purge(const struct auth_serversupplied_info *server_info,
                         struct messaging_context *msg_ctx, int snum)
 {
        print_queue_struct *queue;
@@ -3194,11 +3348,11 @@ WERROR print_queue_purge(struct auth_serversupplied_info *server_info,
 
        for (i=0;i<njobs;i++) {
                bool owner = is_owner(server_info, lp_const_servicename(snum),
-                                     queue[i].job);
+                                     queue[i].sysjob);
 
                if (owner || can_job_admin) {
                        print_job_delete1(server_event_context(), msg_ctx,
-                                         snum, queue[i].job);
+                                         snum, queue[i].sysjob);
                }
        }