From 93042487882d8b2407541ad21d2e9bc2b59142e5 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Thu, 7 Nov 2002 02:15:35 +0000 Subject: [PATCH] Merge of scalable printing code fix... Needs testing. Jeremy. (This used to be commit d030df76439c72825d68410211e62090438cef54) --- source3/Makefile.in | 2 +- source3/printing/notify.c | 89 ++++++--- source3/printing/printing.c | 248 ++++++++++++++++++++++++ source3/rpc_client/cli_spoolss_notify.c | 4 +- source3/rpc_parse/parse_spoolss.c | 2 +- source3/rpc_server/srv_spoolss_nt.c | 38 +++- source3/rpcclient/display_sec.c | 2 +- source3/tdb/tdb.c | 3 +- source3/tdb/tdbutil.c | 19 +- source3/utils/smbcontrol.c | 3 + 10 files changed, 356 insertions(+), 54 deletions(-) diff --git a/source3/Makefile.in b/source3/Makefile.in index 4e220456acb..63cc348eb62 100644 --- a/source3/Makefile.in +++ b/source3/Makefile.in @@ -318,7 +318,7 @@ STATUS_OBJ = utils/status.o $(LOCKING_OBJ) $(PARAM_OBJ) \ $(UBIQX_OBJ) $(PROFILE_OBJ) $(LIB_OBJ) SMBCONTROL_OBJ = utils/smbcontrol.o $(LOCKING_OBJ) $(PARAM_OBJ) \ - $(UBIQX_OBJ) $(PROFILE_OBJ) $(LIB_OBJ) printing/notify.o + $(UBIQX_OBJ) $(PROFILE_OBJ) $(LIB_OBJ) SMBTREE_OBJ = utils/smbtree.o $(LOCKING_OBJ) $(PARAM_OBJ) \ $(UBIQX_OBJ) $(PROFILE_OBJ) $(LIB_OBJ) $(LIBSMB_OBJ) diff --git a/source3/printing/notify.c b/source3/printing/notify.c index 003718ed724..a4111831d9c 100644 --- a/source3/printing/notify.c +++ b/source3/printing/notify.c @@ -26,6 +26,7 @@ static TALLOC_CTX *send_ctx; static struct notify_queue { struct notify_queue *next, *prev; + char *printername; void *buf; size_t buflen; } *notify_queue_head = NULL; @@ -40,33 +41,25 @@ BOOL print_notify_messages_pending(void) } /******************************************************************* - Actually send the batched messages. + Send the batched messages - on a per-printer basis. *******************************************************************/ -void print_notify_send_messages(void) +static void print_notify_send_messages_to_printer(const char *printer) { - TDB_CONTEXT *tdb; char *buf; - struct notify_queue *pq; + struct notify_queue *pq, *pq_next; size_t msg_count = 0, offset = 0; + size_t num_pids = 0; + size_t i; + pid_t *pid_list = NULL; - if (!print_notify_messages_pending()) - return; - - if (!send_ctx) - return; - - tdb = conn_tdb_ctx(); - - if (!tdb) { - DEBUG(3, ("Failed to open connections database in send_spoolss_notify2_msg\n")); - return; - } - /* Count the space needed to send the messages. */ - for (pq = notify_queue_head; pq; pq = pq->next, msg_count++) - offset += (pq->buflen + 4); - + for (pq = notify_queue_head; pq; pq = pq->next) { + if (strequal(printer, pq->printername)) { + offset += (pq->buflen + 4); + msg_count++; + } + } offset += 4; /* For count. */ buf = talloc(send_ctx, offset); @@ -79,19 +72,50 @@ void print_notify_send_messages(void) offset = 0; SIVAL(buf,offset,msg_count); offset += 4; - for (pq = notify_queue_head; pq; pq = pq->next) { - SIVAL(buf,offset,pq->buflen); - offset += 4; - memcpy(buf + offset, pq->buf, pq->buflen); - offset += pq->buflen; + for (pq = notify_queue_head; pq; pq = pq_next) { + pq_next = pq->next; + + if (strequal(printer, pq->printername)) { + SIVAL(buf,offset,pq->buflen); + offset += 4; + memcpy(buf + offset, pq->buf, pq->buflen); + offset += pq->buflen; + + /* Remove from list. */ + DLIST_REMOVE(notify_queue_head, pq); + } } - DEBUG(5, ("print_notify_send_messages: sending %d print notify message%s\n", - msg_count, msg_count != 1 ? "s" : "")); + DEBUG(5, ("print_notify_send_messages_to_printer: sending %d print notify message%s to printer %s\n", + msg_count, msg_count != 1 ? "s" : "", printer)); + + /* + * Get the list of PID's to send to. + */ + + if (!print_notify_pid_list(printer, send_ctx, &num_pids, &pid_list)) + return; + + for (i = 0; i < num_pids; i++) + message_send_pid(pid_list[i], MSG_PRINTER_NOTIFY2, buf, offset, True); +} + +/******************************************************************* + Actually send the batched messages. +*******************************************************************/ + +void print_notify_send_messages(void) +{ + if (!print_notify_messages_pending()) + return; + + if (!send_ctx) + return; + + while (print_notify_messages_pending()) + print_notify_send_messages_to_printer(notify_queue_head->printername); - message_send_all(tdb, MSG_PRINTER_NOTIFY2, buf, offset, False, NULL); talloc_destroy_pool(send_ctx); - notify_queue_head = NULL; } /******************************************************************* @@ -150,10 +174,15 @@ again: if (!pnqueue) goto fail; + pnqueue->printername = talloc_strdup(send_ctx, msg->printer); + if (!pnqueue->printername) + goto fail; + pnqueue->buf = buf; pnqueue->buflen = buflen; - DEBUG(5, ("send_spoolss_notify2_msg: appending message 0x%02x/0x%02x to notify_queue_head\n", msg->type, msg->field)); + DEBUG(5, ("send_spoolss_notify2_msg: appending message 0x%02x/0x%02x for printer %s \ +to notify_queue_head\n", msg->type, msg->field, msg->printer)); /* Note we add to the end of the list to ensure * the messages are sent in the order they were received. JRA. diff --git a/source3/printing/printing.c b/source3/printing/printing.c index 2121fb20a37..6d4cc213a74 100644 --- a/source3/printing/printing.c +++ b/source3/printing/printing.c @@ -1040,6 +1040,254 @@ static void print_queue_update(int snum) release_print_db(pdb); } +/**************************************************************************** + Fetch and clean the pid_t record list for all pids interested in notify + messages. data needs freeing on exit. +****************************************************************************/ + +#define NOTIFY_PID_LIST_KEY "NOTIFY_PID_LIST" + +static TDB_DATA get_printer_notify_pid_list(struct tdb_print_db *pdb) +{ + TDB_DATA data; + size_t i; + + ZERO_STRUCT(data); + + data = tdb_fetch_by_string( pdb->tdb, NOTIFY_PID_LIST_KEY ); + + if (!data.dptr) { + ZERO_STRUCT(data); + return data; + } + + if (data.dsize % 8) { + DEBUG(0,("get_pid_list: Size of record for printer %s not a multiple of 8 !\n", + pdb->printer_name )); + tdb_delete_by_string(pdb->tdb, NOTIFY_PID_LIST_KEY ); + ZERO_STRUCT(data); + return data; + } + + /* + * Weed out all dead entries. + */ + + for( i = 0; i < data.dsize; ) { + pid_t pid = (pid_t)IVAL(data.dptr, i); + + if (pid == sys_getpid()) + continue; + + /* Entry is dead if process doesn't exist or refcount is zero. */ + + if ((IVAL(data.dptr, i + 4) == 0) || !process_exists(pid)) { + + /* Refcount == zero is a logic error and should never happen. */ + if (IVAL(data.dptr, i + 4) == 0) { + DEBUG(0,("get_pid_list: Refcount == 0 for pid = %u printer %s !\n", + (unsigned int)pid, pdb->printer_name )); + } + + if (data.dsize - i > 8) + memmove( &data.dptr[i], &data.dptr[i+8], data.dsize - i - 8); + data.dsize -= 8; + continue; + } + + i += 8; + } + + return data; +} + +/**************************************************************************** + Return a malloced list of pid_t's that are interested in getting update + messages on this print queue. Used in printing/notify to send the messages. +****************************************************************************/ + +BOOL print_notify_pid_list(const char *printername, TALLOC_CTX *mem_ctx, size_t *p_num_pids, pid_t **pp_pid_list) +{ + struct tdb_print_db *pdb; + TDB_DATA data; + BOOL ret = True; + size_t i, num_pids; + pid_t *pid_list; + + *p_num_pids = 0; + *pp_pid_list = NULL; + + pdb = get_print_db_byname(printername); + if (!pdb) + return False; + + if (tdb_lock_bystring(pdb->tdb, NOTIFY_PID_LIST_KEY, 10) == -1) { + DEBUG(0,("print_notify_pid_list: Failed to lock printer %s database\n", printername)); + release_print_db(pdb); + return False; + } + + data = get_printer_notify_pid_list( pdb ); + + if (!data.dptr) { + ret = True; + goto done; + } + + num_pids = data.dsize / 8; + + if ((pid_list = (pid_t *)talloc(mem_ctx, sizeof(pid_t) * num_pids)) == NULL) { + ret = False; + goto done; + } + + for( i = 0; i < data.dsize; i += 8) { + pid_t pid = (pid_t)IVAL(data.dptr, i); + pid_list[i] = pid; + } + + *pp_pid_list = pid_list; + *p_num_pids = num_pids; + + ret = True; + + done: + + tdb_unlock_bystring(pdb->tdb, NOTIFY_PID_LIST_KEY); + release_print_db(pdb); + SAFE_FREE(data.dptr); + return ret; +} + +/**************************************************************************** + Create/Update an entry in the print tdb that will allow us to send notify + updates only to interested smbd's. +****************************************************************************/ + +BOOL print_notify_register_pid(int snum) +{ + TDB_DATA data; + struct tdb_print_db *pdb; + const char *printername = lp_const_servicename(snum); + uint32 mypid = (uint32)sys_getpid(); + BOOL ret = False; + size_t i; + + pdb = get_print_db_byname(printername); + if (!pdb) + return False; + + if (tdb_lock_bystring(pdb->tdb, NOTIFY_PID_LIST_KEY, 10) == -1) { + DEBUG(0,("print_notify_register_pid: Failed to lock printer %s\n", printername)); + release_print_db(pdb); + return False; + } + + data = get_printer_notify_pid_list( pdb ); + + /* Add ourselves and increase the refcount. */ + + for (i = 0; i < data.dsize; i += 8) { + if (IVAL(data.dptr,i) == mypid) { + uint32 new_refcount = IVAL(data.dptr, i+4) + 1; + SIVAL(data.dptr, i+4, new_refcount); + break; + } + } + + if (i == data.dsize) { + /* We weren't in the list. Realloc. */ + data.dptr = Realloc(data.dptr, data.dsize + 8); + if (!data.dptr) { + DEBUG(0,("print_notify_register_pid: Relloc fail for printer %s\n", printername)); + goto done; + } + data.dsize += 8; + SIVAL(data.dptr,data.dsize - 8,mypid); + SIVAL(data.dptr,data.dsize - 4,1); /* Refcount. */ + } + + /* Store back the record. */ + if (tdb_store_by_string(pdb->tdb, NOTIFY_PID_LIST_KEY, data, TDB_REPLACE) == -1) { + DEBUG(0,("print_notify_register_pid: Failed to update pid list for printer %s\n", printername)); + goto done; + } + + ret = True; + + done: + + tdb_unlock_bystring(pdb->tdb, NOTIFY_PID_LIST_KEY); + release_print_db(pdb); + SAFE_FREE(data.dptr); + return ret; +} + +/**************************************************************************** + Update an entry in the print tdb that will allow us to send notify + updates only to interested smbd's. +****************************************************************************/ + +BOOL print_notify_deregister_pid(int snum) +{ + TDB_DATA data; + struct tdb_print_db *pdb; + const char *printername = lp_const_servicename(snum); + uint32 mypid = (uint32)sys_getpid(); + size_t i; + BOOL ret = False; + + pdb = get_print_db_byname(printername); + if (!pdb) + return False; + + if (tdb_lock_bystring(pdb->tdb, NOTIFY_PID_LIST_KEY, 10) == -1) { + DEBUG(0,("print_notify_register_pid: Failed to lock printer %s database\n", printername)); + release_print_db(pdb); + return False; + } + + data = get_printer_notify_pid_list( pdb ); + + /* Reduce refcount. Remove ourselves if zero. */ + + for (i = 0; i < data.dsize; ) { + if (IVAL(data.dptr,i) == mypid) { + uint32 refcount = IVAL(data.dptr, i+4); + + refcount--; + + if (refcount == 0) { + if (data.dsize - i > 8) + memmove( &data.dptr[i], &data.dptr[i+8], data.dsize - i - 8); + data.dsize -= 8; + continue; + } + SIVAL(data.dptr, i+4, refcount); + } + + i += 8; + } + + if (data.dsize == 0) + SAFE_FREE(data.dptr); + + /* Store back the record. */ + if (tdb_store_by_string(pdb->tdb, NOTIFY_PID_LIST_KEY, data, TDB_REPLACE) == -1) { + DEBUG(0,("print_notify_register_pid: Failed to update pid list for printer %s\n", printername)); + goto done; + } + + ret = True; + + done: + + tdb_unlock_bystring(pdb->tdb, NOTIFY_PID_LIST_KEY); + release_print_db(pdb); + SAFE_FREE(data.dptr); + return ret; +} + /**************************************************************************** Check if a jobid is valid. It is valid if it exists in the database. ****************************************************************************/ diff --git a/source3/rpc_client/cli_spoolss_notify.c b/source3/rpc_client/cli_spoolss_notify.c index d07ace8e0cd..2843aaece1d 100644 --- a/source3/rpc_client/cli_spoolss_notify.c +++ b/source3/rpc_client/cli_spoolss_notify.c @@ -37,7 +37,7 @@ notifications are performed. */ WERROR cli_spoolss_reply_open_printer(struct cli_state *cli, TALLOC_CTX *mem_ctx, - char *printer, uint32 printerlocal, uint32 type, + const char *printer, uint32 printerlocal, uint32 type, POLICY_HND *handle) { prs_struct qbuf, rbuf; @@ -227,7 +227,7 @@ done: WERROR cli_spoolss_rffpcnex(struct cli_state *cli, TALLOC_CTX *mem_ctx, POLICY_HND *pol, uint32 flags, uint32 options, - char *localmachine, uint32 printerlocal, + const char *localmachine, uint32 printerlocal, SPOOL_NOTIFY_OPTION *option) { prs_struct qbuf, rbuf; diff --git a/source3/rpc_parse/parse_spoolss.c b/source3/rpc_parse/parse_spoolss.c index 32f0c3a369e..d8cd7c17296 100644 --- a/source3/rpc_parse/parse_spoolss.c +++ b/source3/rpc_parse/parse_spoolss.c @@ -7628,7 +7628,7 @@ BOOL make_spoolss_q_deleteprinterdataex(SPOOL_Q_DELETEPRINTERDATAEX *q_u, ********************************************************************/ BOOL make_spoolss_q_rffpcnex(SPOOL_Q_RFFPCNEX *q_u, POLICY_HND *handle, - uint32 flags, uint32 options, char *localmachine, + uint32 flags, uint32 options, const char *localmachine, uint32 printerlocal, SPOOL_NOTIFY_OPTION *option) { memcpy(&q_u->handle, handle, sizeof(POLICY_HND)); diff --git a/source3/rpc_server/srv_spoolss_nt.c b/source3/rpc_server/srv_spoolss_nt.c index a5e464f73bd..245df2003f1 100644 --- a/source3/rpc_server/srv_spoolss_nt.c +++ b/source3/rpc_server/srv_spoolss_nt.c @@ -178,10 +178,18 @@ static void free_spool_notify_option(SPOOL_NOTIFY_OPTION **pp) Disconnect from the client ****************************************************************************/ -static void srv_spoolss_replycloseprinter(POLICY_HND *handle) +static void srv_spoolss_replycloseprinter(int snum, POLICY_HND *handle) { WERROR result; + /* + * Tell the specific printing tdb we no longer want messages for this printer + * by deregistering our PID. + */ + + if (!print_notify_deregister_pid(snum)) + DEBUG(0,("print_notify_register_pid: Failed to register our pid for printer %s\n", lp_const_servicename(snum) )); + /* weird if the test succeds !!! */ if (smb_connections==0) { DEBUG(0,("srv_spoolss_replycloseprinter:Trying to close non-existant notify backchannel !\n")); @@ -219,7 +227,8 @@ static void free_printer_entry(void *ptr) Printer_entry *Printer = (Printer_entry *)ptr; if (Printer->notify.client_connected==True) - srv_spoolss_replycloseprinter(&Printer->notify.client_hnd); + srv_spoolss_replycloseprinter(print_queue_snum(Printer->dev.handlename), + &Printer->notify.client_hnd); Printer->notify.flags=0; Printer->notify.options=0; @@ -2305,7 +2314,7 @@ done: Connect to the client machine. **********************************************************/ -static BOOL spoolss_connect_to_client(struct cli_state *the_cli, char *remote_machine) +static BOOL spoolss_connect_to_client(struct cli_state *the_cli, const char *remote_machine) { extern pstring global_myname; @@ -2396,7 +2405,7 @@ static BOOL spoolss_connect_to_client(struct cli_state *the_cli, char *remote_ma Connect to the client. ****************************************************************************/ -static BOOL srv_spoolss_replyopenprinter(char *printer, uint32 localprinter, uint32 type, POLICY_HND *handle) +static BOOL srv_spoolss_replyopenprinter(int snum, const char *printer, uint32 localprinter, uint32 type, POLICY_HND *handle) { WERROR result; @@ -2418,6 +2427,14 @@ static BOOL srv_spoolss_replyopenprinter(char *printer, uint32 localprinter, uin register_message_flags( True, FLAG_MSG_PRINTING ); } + /* + * Tell the specific printing tdb we want messages for this printer + * by registering our PID. + */ + + if (!print_notify_register_pid(snum)) + DEBUG(0,("print_notify_register_pid: Failed to register our pid for printer %s\n", printer )); + smb_connections++; result = cli_spoolss_reply_open_printer(¬ify_cli, notify_cli.mem_ctx, printer, localprinter, @@ -2448,6 +2465,7 @@ WERROR _spoolss_rffpcnex(pipes_struct *p, SPOOL_Q_RFFPCNEX *q_u, SPOOL_R_RFFPCNE uint32 options = q_u->options; UNISTR2 *localmachine = &q_u->localmachine; uint32 printerlocal = q_u->printerlocal; + int snum; SPOOL_NOTIFY_OPTION *option = q_u->option; /* store the notify value in the printer struct */ @@ -2459,6 +2477,9 @@ WERROR _spoolss_rffpcnex(pipes_struct *p, SPOOL_Q_RFFPCNEX *q_u, SPOOL_R_RFFPCNE return WERR_BADFID; } + if (!get_printer_snum(p, handle, &snum)) + return WERR_BADFID; + Printer->notify.flags=flags; Printer->notify.options=options; Printer->notify.printerlocal=printerlocal; @@ -2473,7 +2494,7 @@ WERROR _spoolss_rffpcnex(pipes_struct *p, SPOOL_Q_RFFPCNEX *q_u, SPOOL_R_RFFPCNE /* Connect to the client machine and send a ReplyOpenPrinter */ - if(!srv_spoolss_replyopenprinter(Printer->notify.localmachine, + if(!srv_spoolss_replyopenprinter(snum, Printer->notify.localmachine, Printer->notify.printerlocal, 1, &Printer->notify.client_hnd)) return WERR_SERVER_UNAVAILABLE; @@ -5832,7 +5853,7 @@ WERROR _spoolss_setprinter(pipes_struct *p, SPOOL_Q_SETPRINTER *q_u, SPOOL_R_SET WERROR _spoolss_fcpn(pipes_struct *p, SPOOL_Q_FCPN *q_u, SPOOL_R_FCPN *r_u) { POLICY_HND *handle = &q_u->handle; - + int snum; Printer_entry *Printer= find_printer_index_by_hnd(p, handle); if (!Printer) { @@ -5840,8 +5861,11 @@ WERROR _spoolss_fcpn(pipes_struct *p, SPOOL_Q_FCPN *q_u, SPOOL_R_FCPN *r_u) return WERR_BADFID; } + if (!get_printer_snum(p, handle, &snum)) + return WERR_BADFID; + if (Printer->notify.client_connected==True) - srv_spoolss_replycloseprinter(&Printer->notify.client_hnd); + srv_spoolss_replycloseprinter(snum, &Printer->notify.client_hnd); Printer->notify.flags=0; Printer->notify.options=0; diff --git a/source3/rpcclient/display_sec.c b/source3/rpcclient/display_sec.c index 37043c50126..2a93c915f1a 100644 --- a/source3/rpcclient/display_sec.c +++ b/source3/rpcclient/display_sec.c @@ -54,7 +54,7 @@ char *get_sec_mask_str(uint32 type) if (type & DELETE_ACCESS) fstrcat(typestr, "DELETE_ACCESS "); - printf("\t\tSpecific bits: 0x%lx\n", type&SPECIFIC_RIGHTS_MASK); + printf("\t\tSpecific bits: 0x%lx\n", (unsigned long)type&SPECIFIC_RIGHTS_MASK); return typestr; } diff --git a/source3/tdb/tdb.c b/source3/tdb/tdb.c index c57d23cb6f5..2a6dca16a8e 100644 --- a/source3/tdb/tdb.c +++ b/source3/tdb/tdb.c @@ -1442,7 +1442,8 @@ int tdb_store(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, int flag) } memcpy(p, key.dptr, key.dsize); - memcpy(p+key.dsize, dbuf.dptr, dbuf.dsize); + if (dbuf.dsize) + memcpy(p+key.dsize, dbuf.dptr, dbuf.dsize); /* now we're into insert / modify / replace of a record which * we know could not be optimised by an in-place store (for diff --git a/source3/tdb/tdbutil.c b/source3/tdb/tdbutil.c index e7650033b87..ad97f450445 100644 --- a/source3/tdb/tdbutil.c +++ b/source3/tdb/tdbutil.c @@ -218,17 +218,14 @@ BOOL tdb_store_uint32(TDB_CONTEXT *tdb, char *keystr, uint32 value) on failure. ****************************************************************************/ -int tdb_store_by_string(TDB_CONTEXT *tdb, char *keystr, void *buffer, int len) +int tdb_store_by_string(TDB_CONTEXT *tdb, char *keystr, TDB_DATA data, int flags) { - TDB_DATA key, data; + TDB_DATA key; key.dptr = keystr; key.dsize = strlen(keystr) + 1; - data.dptr = buffer; - data.dsize = len; - - return tdb_store(tdb, key, data, TDB_REPLACE); + return tdb_store(tdb, key, data, flags); } /**************************************************************************** @@ -247,17 +244,17 @@ TDB_DATA tdb_fetch_by_string(TDB_CONTEXT *tdb, char *keystr) } /**************************************************************************** - Delete a buffer using a null terminated string key. + Delete an entry using a null terminated string key. ****************************************************************************/ int tdb_delete_by_string(TDB_CONTEXT *tdb, char *keystr) { - TDB_DATA key; + TDB_DATA key; - key.dptr = keystr; - key.dsize = strlen(keystr) + 1; + key.dptr = keystr; + key.dsize = strlen(keystr) + 1; - return tdb_delete(tdb, key); + return tdb_delete(tdb, key); } /**************************************************************************** diff --git a/source3/utils/smbcontrol.c b/source3/utils/smbcontrol.c index 5401755376d..7c292dd521c 100644 --- a/source3/utils/smbcontrol.c +++ b/source3/utils/smbcontrol.c @@ -360,7 +360,9 @@ static BOOL do_command(char *dest, char *msg_name, int iparams, char **params) break; /* Send a notification message to a printer */ + /* NB. None of these currently work due to changes in the printing notify mechanisms. */ +#if 0 case MSG_PRINTER_NOTIFY2: { char *cmd; @@ -462,6 +464,7 @@ static BOOL do_command(char *dest, char *msg_name, int iparams, char **params) break; } +#endif case MSG_SMB_FORCE_TDIS: if (!strequal(dest, "smbd")) { -- 2.34.1