From a834a73e341059be154426390304a42e4a011f72 Mon Sep 17 00:00:00 2001 From: Gerald Carter Date: Wed, 25 Sep 2002 15:19:00 +0000 Subject: sync'ing up for 3.0alpha20 release (This used to be commit 65e7b5273bb58802bf0c389b77f7fcae0a1f6139) --- source3/printing/notify.c | 120 ++++++-- source3/printing/nt_printing.c | 615 +++++++++++++++++++++++++++++------------ source3/printing/print_cups.c | 8 +- source3/printing/printfsp.c | 2 +- source3/printing/printing.c | 427 ++++++++++++++++++++-------- 5 files changed, 864 insertions(+), 308 deletions(-) (limited to 'source3/printing') diff --git a/source3/printing/notify.c b/source3/printing/notify.c index 925d49a21d..003718ed72 100644 --- a/source3/printing/notify.c +++ b/source3/printing/notify.c @@ -22,21 +22,99 @@ #include "printing.h" -/* - * Print notification routines - */ +static TALLOC_CTX *send_ctx; + +static struct notify_queue { + struct notify_queue *next, *prev; + void *buf; + size_t buflen; +} *notify_queue_head = NULL; + +/******************************************************************* + Used to decide if we need a short select timeout. +*******************************************************************/ + +BOOL print_notify_messages_pending(void) +{ + return (notify_queue_head != NULL); +} + +/******************************************************************* + Actually send the batched messages. +*******************************************************************/ + +void print_notify_send_messages(void) +{ + TDB_CONTEXT *tdb; + char *buf; + struct notify_queue *pq; + size_t msg_count = 0, offset = 0; + + 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); + + offset += 4; /* For count. */ + + buf = talloc(send_ctx, offset); + if (!buf) { + DEBUG(0,("print_notify_send_messages: Out of memory\n")); + talloc_destroy_pool(send_ctx); + return; + } + + 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; + } + + DEBUG(5, ("print_notify_send_messages: sending %d print notify message%s\n", + msg_count, msg_count != 1 ? "s" : "")); + + message_send_all(tdb, MSG_PRINTER_NOTIFY2, buf, offset, False, NULL); + talloc_destroy_pool(send_ctx); + notify_queue_head = NULL; +} + +/******************************************************************* + Batch up print notify messages. +*******************************************************************/ static void send_spoolss_notify2_msg(struct spoolss_notify_msg *msg) { char *buf = NULL; - int buflen = 0, len; - TDB_CONTEXT *tdb; + size_t buflen = 0, len; + struct notify_queue *pnqueue, *tmp_ptr; /* Let's not waste any time with this */ if (lp_disable_spoolss()) return; + if (!send_ctx) + send_ctx = talloc_init_named("print notify queue"); + + if (!send_ctx) + goto fail; + /* Flatten data into a message */ again: @@ -59,24 +137,34 @@ again: msg->len, msg->notify.data); if (buflen != len) { - buf = Realloc(buf, len); + buf = talloc_realloc(send_ctx, buf, len); + if (!buf) + goto fail; buflen = len; goto again; } - /* Send message */ + /* Store the message on the pending queue. */ - tdb = conn_tdb_ctx(); + pnqueue = talloc(send_ctx, sizeof(*pnqueue)); + if (!pnqueue) + goto fail; - if (!tdb) { - DEBUG(3, ("Failed to open connections database in send_spoolss_notify2_msg\n")); - goto done; - } - - message_send_all(tdb, MSG_PRINTER_NOTIFY2, buf, buflen, False, NULL); + 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)); + + /* Note we add to the end of the list to ensure + * the messages are sent in the order they were received. JRA. + */ + DLIST_ADD_END(notify_queue_head, pnqueue, tmp_ptr); + + return; + + fail: -done: - SAFE_FREE(buf); + DEBUG(0,("send_spoolss_notify2_msg: Out of memory.\n")); } static void send_notify_field_values(const char *printer_name, uint32 type, diff --git a/source3/printing/nt_printing.c b/source3/printing/nt_printing.c index 3b85fce020..58eba9d87e 100644 --- a/source3/printing/nt_printing.c +++ b/source3/printing/nt_printing.c @@ -1745,7 +1745,7 @@ static WERROR get_a_printer_driver_3(NT_PRINTER_DRIVER_INFO_LEVEL_3 **info_ptr, dbuf = tdb_fetch(tdb_drivers, kbuf); if (!dbuf.dptr) - return WERR_ACCESS_DENIED; + return WERR_UNKNOWN_PRINTER_DRIVER; len += tdb_unpack(dbuf.dptr, dbuf.dsize, "dffffffff", &driver.cversion, @@ -1864,7 +1864,7 @@ static uint32 dump_a_printer_driver(NT_PRINTER_DRIVER_INFO_LEVEL driver, uint32 NT_PRINTER_DRIVER_INFO_LEVEL_3 *info3; int i; - DEBUG(106,("Dumping printer driver at level [%d]\n", level)); + DEBUG(20,("Dumping printer driver at level [%d]\n", level)); switch (level) { @@ -1905,7 +1905,7 @@ static uint32 dump_a_printer_driver(NT_PRINTER_DRIVER_INFO_LEVEL driver, uint32 /**************************************************************************** ****************************************************************************/ -static int pack_devicemode(NT_DEVICEMODE *nt_devmode, char *buf, int buflen) +int pack_devicemode(NT_DEVICEMODE *nt_devmode, char *buf, int buflen) { int len = 0; @@ -2282,7 +2282,7 @@ static void free_nt_printer_info_level_2(NT_PRINTER_INFO_LEVEL_2 **info_ptr) /**************************************************************************** ****************************************************************************/ -static int unpack_devicemode(NT_DEVICEMODE **nt_devmode, char *buf, int buflen) +int unpack_devicemode(NT_DEVICEMODE **nt_devmode, char *buf, int buflen) { int len = 0; int extra_len = 0; @@ -2407,7 +2407,7 @@ int lookup_printerkey( NT_PRINTER_DATA *data, char *name ) for ( i=0; inum_keys; i++ ) { - if ( strcmp(data->keys[i].name, name) == 0 ) { + if ( strequal(data->keys[i].name, name) ) { DEBUG(12,("lookup_printerkey: Found [%s]!\n", name)); key_index = i; break; @@ -2420,32 +2420,169 @@ int lookup_printerkey( NT_PRINTER_DATA *data, char *name ) /**************************************************************************** ***************************************************************************/ + +uint32 get_printer_subkeys( NT_PRINTER_DATA *data, char* key, fstring **subkeys ) +{ + int i, j; + int key_len; + int num_subkeys = 0; + char *p; + fstring *ptr, *subkeys_ptr = NULL; + fstring subkeyname; + + if ( !data ) + return 0; + + for ( i=0; inum_keys; i++ ) + { + if ( StrnCaseCmp(data->keys[i].name, key, strlen(key)) == 0 ) + { + /* match sure it is a subkey and not the key itself */ + + key_len = strlen( key ); + if ( strlen(data->keys[i].name) == key_len ) + continue; + + /* get subkey path */ + + p = data->keys[i].name + key_len; + if ( *p == '\\' ) + p++; + fstrcpy( subkeyname, p ); + if ( (p = strchr( subkeyname, '\\' )) ) + *p = '\0'; + + /* don't add a key more than once */ + + for ( j=0; jdata; + empty_slot = data->num_keys; + + if ( !key ) + return WERR_INVALID_PARAM; - for ( i=0; inum_keys; i++ ) + /* remove all keys */ + + if ( !strlen(key) ) { - DEBUG(8,("delete_all_printer_data: Removed all Printer Data from key [%s]\n", - data->keys[i].name)); + for ( i=0; inum_keys; i++ ) + { + DEBUG(8,("delete_all_printer_data: Removed all Printer Data from key [%s]\n", + data->keys[i].name)); - SAFE_FREE( data->keys[i].name ); - regval_ctr_destroy( &data->keys[i].values ); - } + SAFE_FREE( data->keys[i].name ); + regval_ctr_destroy( &data->keys[i].values ); + } - SAFE_FREE( data->keys ); + DEBUG(8,("delete_all_printer_data: Removed all Printer Data from printer [%s]\n", + p2->printername )); + + SAFE_FREE( data->keys ); + ZERO_STRUCTP( data ); - DEBUG(8,("delete_all_printer_data: Removed all Printer Data from printer [%s]\n", - p2->printername )); + return WERR_OK; + } + + /* remove a specific key (and all subkeys) */ - ZERO_STRUCTP( data ); + for ( i=0; inum_keys; i++ ) + { + if ( StrnCaseCmp( data->keys[i].name, key, strlen(key)) == 0 ) + { + DEBUG(8,("delete_all_printer_data: Removed all Printer Data from key [%s]\n", + data->keys[i].name)); + + SAFE_FREE( data->keys[i].name ); + regval_ctr_destroy( &data->keys[i].values ); + + /* mark the slot as empty */ + + ZERO_STRUCTP( &data->keys[i] ); + } + } + + /* find the first empty slot */ + + for ( i=0; inum_keys; i++ ) { + if ( !data->keys[i].name ) { + empty_slot = i; + removed_keys++; + break; + } + } + + if ( i == data->num_keys ) + /* nothing was removed */ + return WERR_INVALID_PARAM; + + /* move everything down */ - return result; + for ( i=empty_slot+1; inum_keys; i++ ) { + if ( data->keys[i].name ) { + memcpy( &data->keys[empty_slot], &data->keys[i], sizeof(NT_PRINTER_KEY) ); + ZERO_STRUCTP( &data->keys[i] ); + empty_slot++; + removed_keys++; + } + } + + /* update count */ + + data->num_keys -= removed_keys; + + /* sanity check to see if anything is left */ + + if ( !data->num_keys ) + { + DEBUG(8,("delete_all_printer_data: No keys left for printer [%s]\n", p2->printername )); + + SAFE_FREE( data->keys ); + ZERO_STRUCTP( data ); + } + + return WERR_OK; } /**************************************************************************** @@ -2465,11 +2602,8 @@ WERROR delete_printer_data( NT_PRINTER_INFO_LEVEL_2 *p2, char *key, char *value key_index = lookup_printerkey( &p2->data, key ); if ( key_index == -1 ) - key_index = add_new_printer_key( &p2->data, key ); + return WERR_OK; - if ( key_index == -1 ) - return WERR_NOMEM; - regval_ctr_delvalue( &p2->data.keys[key_index].values, value ); DEBUG(8,("delete_printer_data: Removed key => [%s], value => [%s]\n", @@ -2504,8 +2638,8 @@ WERROR add_printer_data( NT_PRINTER_INFO_LEVEL_2 *p2, char *key, char *value, regval_ctr_addvalue( &p2->data.keys[key_index].values, value, type, data, real_len ); - DEBUG(8,("add_printer_data: Added key => [%s], value => [%s], size => [%d]\n", - key, value, real_len )); + DEBUG(8,("add_printer_data: Added key => [%s], value => [%s], type=> [%d], size => [%d]\n", + key, value, type, real_len )); return result; } @@ -2569,7 +2703,7 @@ static int unpack_values(NT_PRINTER_DATA *printer_data, char *buf, int buflen) * Should only be one '\' in the string returned. */ - str = strchr( string, '\\'); + str = strrchr( string, '\\'); /* Put in "PrinterDriverData" is no key specified */ @@ -2598,7 +2732,7 @@ static int unpack_values(NT_PRINTER_DATA *printer_data, char *buf, int buflen) regval_ctr_addvalue( &printer_data->keys[key_index].values, valuename, type, data_p, size ); - DEBUG(8,("specific: [%s\\%s], len: %d\n", keyname, valuename, size)); + DEBUG(8,("specific: [%s:%s], len: %d\n", keyname, valuename, size)); } return len; @@ -3046,7 +3180,7 @@ static BOOL set_driver_init_2( NT_PRINTER_INFO_LEVEL_2 *info_ptr ) * should not be any (if there are delete them). */ - delete_all_printer_data( info_ptr ); + delete_all_printer_data( info_ptr, "" ); slprintf(key, sizeof(key)-1, "%s%s", DRIVER_INIT_PREFIX, info_ptr->drivername); @@ -3074,16 +3208,17 @@ static BOOL set_driver_init_2( NT_PRINTER_INFO_LEVEL_2 *info_ptr ) * the initialization save. Change it to reflect the new printer. */ + if ( info.devmode ) { ZERO_STRUCT(info.devmode->devicename); fstrcpy(info.devmode->devicename, info_ptr->printername); - + } /* * NT/2k does not change out the entire DeviceMode of a printer * when changing the driver. Only the driverextra, private, & * driverversion fields. --jerry (Thu Mar 14 08:58:43 CST 2002) * - * Later e4xamination revealed that Windows NT/2k does reset the + * Later examination revealed that Windows NT/2k does reset the * the printer's device mode, bit **only** when you change a * property of the device mode such as the page orientation. * --jerry @@ -3095,9 +3230,8 @@ static BOOL set_driver_init_2( NT_PRINTER_INFO_LEVEL_2 *info_ptr ) free_nt_devicemode(&info_ptr->devmode); info_ptr->devmode = info.devmode; - - DEBUG(10,("set_driver_init_2: Set printer [%s] init DEVMODE for driver [%s]\n", - info_ptr->printername, info_ptr->drivername)); + DEBUG(10,("set_driver_init_2: Set printer [%s] init %s DEVMODE for driver [%s]\n", + info_ptr->printername, info_ptr->devmode?"VALID":"NULL", info_ptr->drivername)); /* Add the printer data 'values' to the new printer */ @@ -3232,17 +3366,162 @@ uint32 update_driver_init(NT_PRINTER_INFO_LEVEL printer, uint32 level) { case 2: { - result=update_driver_init_2(printer.info_2); + result = update_driver_init_2(printer.info_2); break; } default: - result=1; + result = 1; break; } return result; } +/**************************************************************************** + Convert the printer data value, a REG_BINARY array, into an initialization + DEVMODE. Note: the array must be parsed as if it was a DEVMODE in an rpc... + got to keep the endians happy :). +****************************************************************************/ + +static BOOL convert_driver_init( TALLOC_CTX *ctx, NT_DEVICEMODE *nt_devmode, uint8 *data, uint32 data_len ) +{ + BOOL result = False; + prs_struct ps; + DEVICEMODE devmode; + + ZERO_STRUCT(devmode); + + prs_init(&ps, 0, ctx, UNMARSHALL); + ps.data_p = (char *)data; + ps.buffer_size = data_len; + + if (spoolss_io_devmode("phantom DEVMODE", &ps, 0, &devmode)) + result = convert_devicemode("", &devmode, &nt_devmode); + else + DEBUG(10,("convert_driver_init: error parsing DEVMODE\n")); + + return result; +} + +/**************************************************************************** + Set the DRIVER_INIT info in the tdb. Requires Win32 client code that: + + 1. Use the driver's config DLL to this UNC printername and: + a. Call DrvPrintEvent with PRINTER_EVENT_INITIALIZE + b. Call DrvConvertDevMode with CDM_DRIVER_DEFAULT to get default DEVMODE + 2. Call SetPrinterData with the 'magic' key and the DEVMODE as data. + + The last step triggers saving the "driver initialization" information for + this printer into the tdb. Later, new printers that use this driver will + have this initialization information bound to them. This simulates the + driver initialization, as if it had run on the Samba server (as it would + have done on NT). + + The Win32 client side code requirement sucks! But until we can run arbitrary + Win32 printer driver code on any Unix that Samba runs on, we are stuck with it. + + It would have been easier to use SetPrinter because all the UNMARSHALLING of + the DEVMODE is done there, but 2K/XP clients do not set the DEVMODE... think + about it and you will realize why. JRR 010720 +****************************************************************************/ + +static WERROR save_driver_init_2(NT_PRINTER_INFO_LEVEL *printer, uint8 *data, uint32 data_len ) +{ + WERROR status = WERR_OK; + TALLOC_CTX *ctx = NULL; + NT_DEVICEMODE *nt_devmode = NULL; + NT_DEVICEMODE *tmp_devmode = printer->info_2->devmode; + + /* + * When the DEVMODE is already set on the printer, don't try to unpack it. + */ + DEBUG(8,("save_driver_init_2: Enter...\n")); + + if ( !printer->info_2->devmode && data_len ) + { + /* + * Set devmode on printer info, so entire printer initialization can be + * saved to tdb. + */ + + if ((ctx = talloc_init()) == NULL) + return WERR_NOMEM; + + if ((nt_devmode = (NT_DEVICEMODE*)malloc(sizeof(NT_DEVICEMODE))) == NULL) { + status = WERR_NOMEM; + goto done; + } + + ZERO_STRUCTP(nt_devmode); + + /* + * The DEVMODE is held in the 'data' component of the param in raw binary. + * Convert it to to a devmode structure + */ + if ( !convert_driver_init( ctx, nt_devmode, data, data_len )) { + DEBUG(10,("save_driver_init_2: error converting DEVMODE\n")); + status = WERR_INVALID_PARAM; + goto done; + } + + printer->info_2->devmode = nt_devmode; + } + + /* + * Pack up and add (or update) the DEVMODE and any current printer data to + * a 'driver init' element in the tdb + * + */ + + if ( update_driver_init(*printer, 2) != 0 ) { + DEBUG(10,("save_driver_init_2: error updating DEVMODE\n")); + status = WERR_NOMEM; + goto done; + } + + /* + * If driver initialization info was successfully saved, set the current + * printer to match it. This allows initialization of the current printer + * as well as the driver. + */ + status = mod_a_printer(*printer, 2); + if (!W_ERROR_IS_OK(status)) { + DEBUG(10,("save_driver_init_2: error setting DEVMODE on printer [%s]\n", + printer->info_2->printername)); + } + + done: + talloc_destroy(ctx); + free_nt_devicemode( &nt_devmode ); + + printer->info_2->devmode = tmp_devmode; + + return status; +} + +/**************************************************************************** + Update the driver init info (DEVMODE and specifics) for a printer +****************************************************************************/ + +WERROR save_driver_init(NT_PRINTER_INFO_LEVEL *printer, uint32 level, uint8 *data, uint32 data_len) +{ + WERROR status = WERR_OK; + + switch (level) + { + case 2: + { + status = save_driver_init_2( printer, data, data_len ); + break; + } + default: + status = WERR_UNKNOWN_LEVEL; + break; + } + + return status; +} + /**************************************************************************** Get a NT_PRINTER_INFO_LEVEL struct. It returns malloced memory. ****************************************************************************/ @@ -3445,13 +3724,13 @@ uint32 free_a_printer_driver(NT_PRINTER_DRIVER_INFO_LEVEL driver, uint32 level) to a printer ****************************************************************************/ -BOOL printer_driver_in_use ( NT_PRINTER_DRIVER_INFO_LEVEL_3 *i ) +BOOL printer_driver_in_use ( NT_PRINTER_DRIVER_INFO_LEVEL_3 *info_3 ) { int snum; int n_services = lp_numservices(); NT_PRINTER_INFO_LEVEL *printer = NULL; - if ( !i ) + if ( !info_3 ) return False; DEBUG(5,("printer_driver_in_use: Beginning search through ntprinters.tdb...\n")); @@ -3466,7 +3745,7 @@ BOOL printer_driver_in_use ( NT_PRINTER_DRIVER_INFO_LEVEL_3 *i ) if ( !W_ERROR_IS_OK(get_a_printer(&printer, 2, lp_servicename(snum))) ) continue; - if ( !StrCaseCmp(i->name, printer->info_2->drivername) ) { + if ( !StrCaseCmp(info_3->name, printer->info_2->drivername) ) { free_a_printer( &printer, 2 ); return True; } @@ -3488,7 +3767,7 @@ BOOL printer_driver_in_use ( NT_PRINTER_DRIVER_INFO_LEVEL_3 *i ) static BOOL drv_file_in_use( char* file, NT_PRINTER_DRIVER_INFO_LEVEL_3 *info ) { - char *s; + int i = 0; if ( !info ) return False; @@ -3504,16 +3783,18 @@ static BOOL drv_file_in_use( char* file, NT_PRINTER_DRIVER_INFO_LEVEL_3 *info ) if ( strequal(file, info->helpfile) ) return True; - - s = (char*) info->dependentfiles; - if ( s ) { - while ( *s ) - { - if ( strequal(file, s) ) - return True; - s += strlen(s) + 1; - } + /* see of there are any dependent files to examine */ + + if ( !info->dependentfiles ) + return False; + + while ( *info->dependentfiles[i] ) + { + if ( strequal(file, info->dependentfiles[i]) ) + return True; + + i++; } return False; @@ -3525,27 +3806,20 @@ static BOOL drv_file_in_use( char* file, NT_PRINTER_DRIVER_INFO_LEVEL_3 *info ) input parameter from the list *********************************************************************/ -static void trim_dependent_file( char* s ) +static void trim_dependent_file( fstring files[], int idx ) { - char *p; - - /* set p to the next character string in the list */ - - p = s + strlen( s ) + 1; - - /* check to see that we have another string to copy back */ - if ( *p == '\0' ) + /* bump everything down a slot */ + + while( *files[idx+1] ) { - /* loop over s copying characters from p to s */ - while ( *p!='\0' && *(p+1)!='\0' ) - *s++ = *p++; + fstrcpy( files[idx], files[idx+1] ); + idx++; } - /* add the two trailing NULL's */ - - *s = '\0'; - *(s+1) = '\0'; + *files[idx] = '\0'; + + return; } /********************************************************************** @@ -3555,8 +3829,8 @@ static void trim_dependent_file( char* s ) static BOOL trim_overlap_drv_files( NT_PRINTER_DRIVER_INFO_LEVEL_3 *src, NT_PRINTER_DRIVER_INFO_LEVEL_3 *drv ) { - BOOL in_use = False; - char *s; + BOOL in_use = False; + int i = 0; if ( !src || !drv ) return False; @@ -3565,33 +3839,43 @@ static BOOL trim_overlap_drv_files( NT_PRINTER_DRIVER_INFO_LEVEL_3 *src, if ( drv_file_in_use(src->driverpath, drv) ) { in_use = True; + DEBUG(10,("Removing driverfile [%s] from list\n", src->driverpath)); fstrcpy( src->driverpath, "" ); } if ( drv_file_in_use(src->datafile, drv) ) { in_use = True; + DEBUG(10,("Removing datafile [%s] from list\n", src->datafile)); fstrcpy( src->datafile, "" ); } if ( drv_file_in_use(src->configfile, drv) ) { in_use = True; + DEBUG(10,("Removing configfile [%s] from list\n", src->configfile)); fstrcpy( src->configfile, "" ); } - s = (char*)src->dependentfiles; - - if ( s ) { - while ( *s ) - { - if ( drv_file_in_use(s, drv) ) { - in_use = True; - trim_dependent_file( s ); - } - else - s += strlen(s) + 1; - } + if ( drv_file_in_use(src->helpfile, drv) ) { + in_use = True; + DEBUG(10,("Removing helpfile [%s] from list\n", src->helpfile)); + fstrcpy( src->helpfile, "" ); } + + /* are there any dependentfiles to examine? */ + + if ( !src->dependentfiles ) + return in_use; + while ( *src->dependentfiles[i] ) + { + if ( drv_file_in_use(src->dependentfiles[i], drv) ) { + in_use = True; + DEBUG(10,("Removing [%s] from dependent file list\n", src->dependentfiles[i])); + trim_dependent_file( src->dependentfiles, i ); + } + else + i++; + } return in_use; } @@ -3615,59 +3899,62 @@ BOOL printer_driver_files_in_use ( NT_PRINTER_DRIVER_INFO_LEVEL_3 *info ) fstring *list = NULL; NT_PRINTER_DRIVER_INFO_LEVEL driver; + if ( !info ) + return False; + + version = info->cversion; + /* loop over all driver versions */ DEBUG(5,("printer_driver_files_in_use: Beginning search through ntdrivers.tdb...\n")); - for ( version=0; versionenvironment, version); + list = NULL; + ndrivers = get_ntdrivers(&list, info->environment, version); - DEBUGADD(4,("we have:[%d] drivers in environment [%s] and version [%d]\n", - ndrivers, info->environment, version)); + DEBUGADD(4,("we have:[%d] drivers in environment [%s] and version [%d]\n", + ndrivers, info->environment, version)); - if (ndrivers == -1) - continue; - - /* check each driver for overlap in files */ + /* check each driver for overlap in files */ - for (i=0; ienvironment, version)) ) - { - SAFE_FREE(list); - return True; - } + if ( !W_ERROR_IS_OK(get_a_printer_driver(&driver, 3, list[i], + info->environment, version)) ) + { + SAFE_FREE(list); + return True; + } - /* check if d2 uses any files from d1 */ - /* only if this is a different driver than the one being deleted */ + /* check if d2 uses any files from d1 */ + /* only if this is a different driver than the one being deleted */ - if ( !strequal(info->name, driver.info_3->name) - || (info->cversion != driver.info_3->cversion) ) - { - if ( trim_overlap_drv_files(info, driver.info_3) ) { - free_a_printer_driver(driver, 3); - SAFE_FREE( list ); - return True; - } + if ( !strequal(info->name, driver.info_3->name) ) + { + if ( trim_overlap_drv_files(info, driver.info_3) ) { + free_a_printer_driver(driver, 3); + SAFE_FREE( list ); + return True; } + } - free_a_printer_driver(driver, 3); - } - - SAFE_FREE(list); - } + free_a_printer_driver(driver, 3); + } + + SAFE_FREE(list); DEBUG(5,("printer_driver_files_in_use: Completed search through ntdrivers.tdb...\n")); + driver.info_3 = info; + + if ( DEBUGLEVEL >= 20 ) + dump_a_printer_driver( driver, 3 ); + return False; } @@ -3677,17 +3964,18 @@ BOOL printer_driver_files_in_use ( NT_PRINTER_DRIVER_INFO_LEVEL_3 *info ) this. ****************************************************************************/ -static BOOL delete_driver_files( NT_PRINTER_DRIVER_INFO_LEVEL_3 *i, struct current_user *user ) +static BOOL delete_driver_files( NT_PRINTER_DRIVER_INFO_LEVEL_3 *info_3, struct current_user *user ) { + int i = 0; char *s; connection_struct *conn; DATA_BLOB null_pw; NTSTATUS nt_status; - if ( !i ) + if ( !info_3 ) return False; - DEBUG(6,("delete_driver_files: deleting driver [%s] - version [%d]\n", i->name, i->cversion)); + DEBUG(6,("delete_driver_files: deleting driver [%s] - version [%d]\n", info_3->name, info_3->cversion)); /* * Connect to the print$ share under the same account as the @@ -3715,49 +4003,55 @@ static BOOL delete_driver_files( NT_PRINTER_DRIVER_INFO_LEVEL_3 *i, struct curre /* now delete the files; must strip the '\print$' string from fron of path */ - if ( *i->driverpath ) { - if ( (s = strchr( &i->driverpath[1], '\\' )) != NULL ) { + if ( *info_3->driverpath ) { + if ( (s = strchr( &info_3->driverpath[1], '\\' )) != NULL ) { DEBUG(10,("deleting driverfile [%s]\n", s)); unlink_internals(conn, 0, s); } } - if ( *i->configfile ) { - if ( (s = strchr( &i->configfile[1], '\\' )) != NULL ) { + if ( *info_3->configfile ) { + if ( (s = strchr( &info_3->configfile[1], '\\' )) != NULL ) { DEBUG(10,("deleting configfile [%s]\n", s)); unlink_internals(conn, 0, s); } } - if ( *i->datafile ) { - if ( (s = strchr( &i->datafile[1], '\\' )) != NULL ) { + if ( *info_3->datafile ) { + if ( (s = strchr( &info_3->datafile[1], '\\' )) != NULL ) { DEBUG(10,("deleting datafile [%s]\n", s)); unlink_internals(conn, 0, s); } } - if ( *i->helpfile ) { - if ( (s = strchr( &i->helpfile[1], '\\' )) != NULL ) { + if ( *info_3->helpfile ) { + if ( (s = strchr( &info_3->helpfile[1], '\\' )) != NULL ) { DEBUG(10,("deleting helpfile [%s]\n", s)); unlink_internals(conn, 0, s); } } - s = (char*)i->dependentfiles; + /* check if we are done removing files */ - while ( s && *s ) { - char *file; + if ( info_3->dependentfiles ) + { + while ( *info_3->dependentfiles[i] ) { + char *file; - if ( (file = strchr( s+1, '\\' )) != NULL ) - { - DEBUG(10,("deleting dependent file [%s]\n", file)); - unlink_internals(conn, 0, file ); - file += strlen( file ) + 1; + /* bypass the "\print$" portion of the path */ + + if ( (file = strchr( info_3->dependentfiles[i]+1, '\\' )) != NULL ) + { + DEBUG(10,("deleting dependent file [%s]\n", file)); + unlink_internals(conn, 0, file ); + } + + i++; } - - s = file; } + unbecome_user(); + return True; } @@ -3766,7 +4060,7 @@ static BOOL delete_driver_files( NT_PRINTER_DRIVER_INFO_LEVEL_3 *i, struct curre previously looked up. ***************************************************************************/ -static WERROR delete_printer_driver_internal( NT_PRINTER_DRIVER_INFO_LEVEL_3 *i, struct current_user *user, +WERROR delete_printer_driver( NT_PRINTER_DRIVER_INFO_LEVEL_3 *info_3, struct current_user *user, uint32 version, BOOL delete_files ) { pstring key; @@ -3776,14 +4070,14 @@ static WERROR delete_printer_driver_internal( NT_PRINTER_DRIVER_INFO_LEVEL_3 *i, /* delete the tdb data first */ - get_short_archi(arch, i->environment); + get_short_archi(arch, info_3->environment); slprintf(key, sizeof(key)-1, "%s%s/%d/%s", DRIVERS_PREFIX, - arch, version, i->name); + arch, version, info_3->name); DEBUG(5,("delete_printer_driver: key = [%s] delete_files = %s\n", key, delete_files ? "TRUE" : "FALSE" )); - ctr.info_3 = i; + ctr.info_3 = info_3; dump_a_printer_driver( ctr, 3 ); kbuf.dptr=key; @@ -3793,7 +4087,7 @@ static WERROR delete_printer_driver_internal( NT_PRINTER_DRIVER_INFO_LEVEL_3 *i, dbuf = tdb_fetch( tdb_drivers, kbuf ); if ( !dbuf.dptr ) { - DEBUG(8,("delete_printer_driver_internal: Driver unknown [%s]\n", key)); + DEBUG(8,("delete_printer_driver: Driver unknown [%s]\n", key)); return WERR_UNKNOWN_PRINTER_DRIVER; } @@ -3802,7 +4096,7 @@ static WERROR delete_printer_driver_internal( NT_PRINTER_DRIVER_INFO_LEVEL_3 *i, /* ok... the driver exists so the delete should return success */ if (tdb_delete(tdb_drivers, kbuf) == -1) { - DEBUG (0,("delete_printer_driver_internal: fail to delete %s!\n", key)); + DEBUG (0,("delete_printer_driver: fail to delete %s!\n", key)); return WERR_ACCESS_DENIED; } @@ -3813,51 +4107,14 @@ static WERROR delete_printer_driver_internal( NT_PRINTER_DRIVER_INFO_LEVEL_3 *i, */ if ( delete_files ) - delete_driver_files( i, user ); - - - DEBUG(5,("delete_printer_driver_internal: driver delete successful [%s]\n", key)); - - return WERR_OK; -} - -/**************************************************************************** - Remove a printer driver from the TDB. This assumes that the the driver was - previously looked up. - ***************************************************************************/ - -WERROR delete_printer_driver( NT_PRINTER_DRIVER_INFO_LEVEL_3 *i, struct current_user *user, - uint32 version, BOOL delete_files ) -{ - WERROR err; - - /* - * see if we should delete all versions of this driver - * (DRIVER_ANY_VERSION uis only set for "Windows NT x86") - */ - - if ( version == DRIVER_ANY_VERSION ) - { - /* Windows NT 4.0 */ - - err = delete_printer_driver_internal(i, user, 2, delete_files ); - if ( !W_ERROR_IS_OK(err) && (W_ERROR_V(err) != ERRunknownprinterdriver ) ) - return err; + delete_driver_files( info_3, user ); - /* Windows 2000/XP */ - err = delete_printer_driver_internal(i, user, 3, delete_files ); - if ( !W_ERROR_IS_OK(err) && (W_ERROR_V(err) != ERRunknownprinterdriver ) ) - return err; + DEBUG(5,("delete_printer_driver: driver delete successful [%s]\n", key)); return WERR_OK; } - /* just delete what they asked for */ - - return delete_printer_driver_internal(i, user, version, delete_files ); -} - /**************************************************************************** Store a security desc for a printer. ****************************************************************************/ diff --git a/source3/printing/print_cups.c b/source3/printing/print_cups.c index 51ebb739a3..2df846aa57 100644 --- a/source3/printing/print_cups.c +++ b/source3/printing/print_cups.c @@ -73,9 +73,9 @@ cups_passwd_cb(const char *prompt) /* I - Prompt */ * system. */ -void -cups_printer_fn(void (*fn)(char *, char *)) /* I - Function to call */ +void cups_printer_fn(void (*fn)(char *, char *)) { + /* I - Function to call */ http_t *http; /* HTTP connection to server */ ipp_t *request, /* IPP Request */ *response; /* IPP Response */ @@ -665,6 +665,10 @@ cups_job_submit(int snum, struct printjob *pjob) httpClose(http); + if ( ret == 0 ) + unlink(pjob->filename); + /* else print_job_end will do it for us */ + return (ret); } diff --git a/source3/printing/printfsp.c b/source3/printing/printfsp.c index ff50ac47c4..8a4e7ea073 100644 --- a/source3/printing/printfsp.c +++ b/source3/printing/printfsp.c @@ -46,7 +46,7 @@ files_struct *print_fsp_open(connection_struct *conn, char *fname) fstrcat(name, p); } - jobid = print_job_start(¤t_user, SNUM(conn), name); + jobid = print_job_start(¤t_user, SNUM(conn), name, NULL); if (jobid == -1) { file_free(fsp); return NULL; diff --git a/source3/printing/printing.c b/source3/printing/printing.c index cb689c05d6..6474c92c69 100644 --- a/source3/printing/printing.c +++ b/source3/printing/printing.c @@ -132,11 +132,13 @@ static pid_t local_pid; static int get_queue_status(int, print_status_struct *); +/* There can be this many printing tdb's open, plus any locked ones. */ #define MAX_PRINT_DBS_OPEN 1 struct tdb_print_db { struct tdb_print_db *next, *prev; TDB_CONTEXT *tdb; + int ref_count; fstring printer_name; }; @@ -149,32 +151,45 @@ static struct tdb_print_db *print_db_head; static struct tdb_print_db *get_print_db_byname(const char *printername) { - struct tdb_print_db *p, *last_entry; + struct tdb_print_db *p = NULL, *last_entry = NULL; int num_open = 0; pstring printdb_path; for (p = print_db_head, last_entry = print_db_head; p; p = p->next) { if (p->tdb && strequal(p->printer_name, printername)) { DLIST_PROMOTE(print_db_head, p); + p->ref_count++; return p; } num_open++; last_entry = p; } + /* Not found. */ if (num_open >= MAX_PRINT_DBS_OPEN) { - /* Recycle the last entry. */ + /* Try and recycle the last entry. */ DLIST_PROMOTE(print_db_head, last_entry); - if (print_db_head->tdb) { - if (tdb_close(print_db_head->tdb)) { - DEBUG(0,("get_print_db: Failed to close tdb for printer %s\n", - print_db_head->printer_name )); - return NULL; + + for (p = print_db_head; p; p = p->next) { + if (p->ref_count) + continue; + if (p->tdb) { + if (tdb_close(print_db_head->tdb)) { + DEBUG(0,("get_print_db: Failed to close tdb for printer %s\n", + print_db_head->printer_name )); + return NULL; + } } + ZERO_STRUCTP(p); + break; } - p = print_db_head; - ZERO_STRUCTP(p); - } else { + if (p) { + DLIST_PROMOTE(print_db_head, p); + p = print_db_head; + } + } + + if (!p) { /* Create one. */ p = (struct tdb_print_db *)malloc(sizeof(struct tdb_print_db)); if (!p) { @@ -201,9 +216,16 @@ static struct tdb_print_db *get_print_db_byname(const char *printername) return NULL; } fstrcpy(p->printer_name, printername); + p->ref_count++; return p; } +static void release_print_db( struct tdb_print_db *pdb) +{ + pdb->ref_count--; + SMB_ASSERT(pdb->ref_count >= 0); +} + /**************************************************************************** Initialise the printing backend. Called once at startup. Does not survive a fork @@ -235,7 +257,10 @@ BOOL print_backend_init(void) pdb = get_print_db_byname(lp_const_servicename(snum)); if (!pdb) continue; - tdb_lock_bystring(pdb->tdb, sversion); + if (tdb_lock_bystring(pdb->tdb, sversion) == -1) { + DEBUG(0,("print_backend_init: Failed to open printer %s database\n", lp_const_servicename(snum) )); + return False; + } if (tdb_fetch_int32(pdb->tdb, sversion) != PRINT_DATABASE_VERSION) { tdb_traverse(pdb->tdb, tdb_traverse_delete_fn, NULL); tdb_store_int32(pdb->tdb, sversion, PRINT_DATABASE_VERSION); @@ -286,25 +311,74 @@ static TDB_DATA print_key(uint32 jobid) return ret; } +/*********************************************************************** + unpack a pjob from a tdb buffer +***********************************************************************/ + +int unpack_pjob( char* buf, int buflen, struct printjob *pjob ) +{ + int len = 0; + int used; + + if ( !buf || !pjob ) + return -1; + + len += tdb_unpack(buf+len, buflen-len, "dddddddddffff", + &pjob->pid, + &pjob->sysjob, + &pjob->fd, + &pjob->starttime, + &pjob->status, + &pjob->size, + &pjob->page_count, + &pjob->spooled, + &pjob->smbjob, + pjob->filename, + pjob->jobname, + pjob->user, + pjob->queuename); + + if ( len == -1 ) + return -1; + + if ( (used = unpack_devicemode(&pjob->nt_devmode, buf+len, buflen-len)) == -1 ) + return -1; + + len += used; + + return len; + +} + /**************************************************************************** Useful function to find a print job in the database. ****************************************************************************/ static struct printjob *print_job_find(int snum, uint32 jobid) { - static struct printjob pjob; - TDB_DATA ret; - struct tdb_print_db *pdb = get_print_db_byname(lp_const_servicename(snum)); + static struct printjob pjob; + TDB_DATA ret; + struct tdb_print_db *pdb = get_print_db_byname(lp_const_servicename(snum)); + if (!pdb) return NULL; ret = tdb_fetch(pdb->tdb, print_key(jobid)); - if (!ret.dptr || ret.dsize != sizeof(pjob)) - return NULL; + release_print_db(pdb); - memcpy(&pjob, ret.dptr, sizeof(pjob)); - SAFE_FREE(ret.dptr); + if (!ret.dptr) + return NULL; + + if ( pjob.nt_devmode ) + free_nt_devicemode( &pjob.nt_devmode ); + + ZERO_STRUCT( pjob ); + + if ( unpack_pjob( ret.dptr, ret.dsize, &pjob ) == -1 ) + return NULL; + + SAFE_FREE(ret.dptr); return &pjob; } @@ -315,9 +389,13 @@ static uint32 sysjob_to_jobid_value; static int unixjob_traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA data, void *state) { - struct printjob *pjob = (struct printjob *)data.dptr; + struct printjob *pjob; int *sysjob = (int *)state; + if (!data.dptr || data.dsize == 0) + return 0; + + pjob = (struct printjob *)data.dptr; if (key.dsize != sizeof(uint32)) return 0; @@ -350,6 +428,7 @@ uint32 sysjob_to_jobid(int unix_jobid) pdb = get_print_db_byname(lp_const_servicename(snum)); if (pdb) tdb_traverse(pdb->tdb, unixjob_traverse_fn, &unix_jobid); + release_print_db(pdb); if (sysjob_to_jobid_value != (uint32)-1) return sysjob_to_jobid_value; } @@ -434,9 +513,12 @@ static void pjob_store_notify(int snum, uint32 jobid, struct printjob *old_data, static BOOL pjob_store(int snum, uint32 jobid, struct printjob *pjob) { - TDB_DATA old_data, new_data; - BOOL ret; - struct tdb_print_db *pdb = get_print_db_byname(lp_const_servicename(snum)); + TDB_DATA old_data, new_data; + BOOL ret = False; + struct tdb_print_db *pdb = get_print_db_byname(lp_const_servicename(snum)); + char *buf = NULL; + int len, newlen, buflen; + if (!pdb) return False; @@ -445,20 +527,63 @@ static BOOL pjob_store(int snum, uint32 jobid, struct printjob *pjob) old_data = tdb_fetch(pdb->tdb, print_key(jobid)); + /* Doh! Now we have to pack/unpack data since the NT_DEVICEMODE was added */ + + newlen = 0; + + do { + len = 0; + buflen = newlen; + len += tdb_pack(buf+len, buflen-len, "dddddddddffff", + pjob->pid, + pjob->sysjob, + pjob->fd, + pjob->starttime, + pjob->status, + pjob->size, + pjob->page_count, + pjob->spooled, + pjob->smbjob, + pjob->filename, + pjob->jobname, + pjob->user, + pjob->queuename); + + len += pack_devicemode(pjob->nt_devmode, buf+len, buflen-len); + + if (buflen != len) + { + char *tb; + + tb = (char *)Realloc(buf, len); + if (!tb) { + DEBUG(0,("pjob_store: failed to enlarge buffer!\n")); + goto done; + } + else + buf = tb; + newlen = len; + } + } + while ( buflen != len ); + + /* Store new data */ - new_data.dptr = (void *)pjob; - new_data.dsize = sizeof(*pjob); + new_data.dptr = buf; + new_data.dsize = len; ret = (tdb_store(pdb->tdb, print_key(jobid), new_data, TDB_REPLACE) == 0); + release_print_db(pdb); + /* Send notify updates for what has changed */ - if (ret && (old_data.dsize == 0 || old_data.dsize == sizeof(*pjob))) { - pjob_store_notify( - snum, jobid, (struct printjob *)old_data.dptr, - (struct printjob *)new_data.dptr); - free(old_data.dptr); - } + if ( ret && (old_data.dsize == 0 || old_data.dsize == sizeof(*pjob)) ) + pjob_store_notify( snum, jobid, (struct printjob *)old_data.dptr, pjob ); + +done: + SAFE_FREE( old_data.dptr ); + SAFE_FREE( buf ); return ret; } @@ -479,6 +604,7 @@ static void pjob_delete(int snum, uint32 jobid) if (!pjob) { DEBUG(5, ("pjob_delete(): we were asked to delete nonexistent job %u\n", (unsigned int)jobid)); + release_print_db(pdb); return; } @@ -499,6 +625,7 @@ static void pjob_delete(int snum, uint32 jobid) /* Remove from printing.tdb */ tdb_delete(pdb->tdb, print_key(jobid)); + release_print_db(pdb); rap_jobid_delete(snum, jobid); } @@ -569,10 +696,14 @@ static int traverse_fn_delete(TDB_CONTEXT *t, TDB_DATA key, TDB_DATA data, void uint32 jobid; int i; - if (data.dsize != sizeof(pjob) || key.dsize != sizeof(jobid)) + if ( key.dsize != sizeof(jobid) ) return 0; + memcpy(&jobid, key.dptr, sizeof(jobid)); - memcpy(&pjob, data.dptr, sizeof(pjob)); + if ( unpack_pjob( data.dptr, data.dsize, &pjob ) == -1 ) + return 0; + free_nt_devicemode( &pjob.nt_devmode ); + if (ts->snum != lp_servicenumber(pjob.queuename)) { /* this isn't for the queue we are looking at - this cannot happen with the split tdb's. JRA */ @@ -651,6 +782,7 @@ static void print_cache_flush(int snum) return; slprintf(key, sizeof(key)-1, "CACHE/%s", printername); tdb_store_int32(pdb->tdb, key, -1); + release_print_db(pdb); } /**************************************************************************** @@ -671,6 +803,7 @@ static pid_t get_updating_pid(fstring printer_name) key.dsize = strlen(keystr); data = tdb_fetch(pdb->tdb, key); + release_print_db(pdb); if (!data.dptr || data.dsize != sizeof(pid_t)) return (pid_t)-1; @@ -705,6 +838,7 @@ static void set_updating_pid(const fstring printer_name, BOOL delete) if (delete) { tdb_delete(pdb->tdb, key); + release_print_db(pdb); return; } @@ -712,6 +846,7 @@ static void set_updating_pid(const fstring printer_name, BOOL delete) data.dsize = sizeof(pid_t); tdb_store(pdb->tdb, key, data, TDB_REPLACE); + release_print_db(pdb); } /**************************************************************************** @@ -740,13 +875,19 @@ static void print_queue_update(int snum) * This is essentially a mutex on the update. */ - if (get_updating_pid(printer_name) != -1) + if (get_updating_pid(printer_name) != -1) { + release_print_db(pdb); return; + } /* Lock the queue for the database update */ slprintf(keystr, sizeof(keystr) - 1, "LOCK/%s", printer_name); - tdb_lock_bystring(pdb->tdb, keystr); + if (tdb_lock_bystring(pdb->tdb, keystr) == -1) { + DEBUG(0,("print_queue_update: Failed to lock printer %s database\n", printer_name)); + release_print_db(pdb); + return; + } /* * Ensure that no one else got in here. @@ -759,6 +900,7 @@ static void print_queue_update(int snum) * Someone else is doing the update, exit. */ tdb_unlock_bystring(pdb->tdb, keystr); + release_print_db(pdb); return; } @@ -865,6 +1007,7 @@ static void print_queue_update(int snum) /* Delete our pid from the db. */ set_updating_pid(printer_name, True); + release_print_db(pdb); } /**************************************************************************** @@ -874,9 +1017,13 @@ static void print_queue_update(int snum) BOOL print_job_exists(int snum, uint32 jobid) { struct tdb_print_db *pdb = get_print_db_byname(lp_const_servicename(snum)); + BOOL ret; + if (!pdb) return False; - return tdb_exists(pdb->tdb, print_key(jobid)); + ret = tdb_exists(pdb->tdb, print_key(jobid)); + release_print_db(pdb); + return ret; } /**************************************************************************** @@ -908,6 +1055,23 @@ char *print_job_fname(int snum, uint32 jobid) return pjob->filename; } + +/**************************************************************************** + Give the filename used for a jobid. + Only valid for the process doing the spooling and when the job + has not been spooled. +****************************************************************************/ + +NT_DEVICEMODE *print_job_devmode(int snum, uint32 jobid) +{ + struct printjob *pjob = print_job_find(snum, jobid); + + if ( !pjob ) + return NULL; + + return pjob->nt_devmode; +} + /**************************************************************************** Set the place in the queue for a job. ****************************************************************************/ @@ -1000,8 +1164,11 @@ static BOOL is_owner(struct current_user *user, int snum, uint32 jobid) BOOL print_job_delete(struct current_user *user, int snum, uint32 jobid, WERROR *errcode) { - BOOL owner; + BOOL owner, deleted; + char *fname; + *errcode = WERR_OK; + owner = is_owner(user, snum, jobid); /* Check access against security descriptor or whether the user @@ -1014,15 +1181,40 @@ BOOL print_job_delete(struct current_user *user, int snum, uint32 jobid, WERROR return False; } - if (!print_job_delete1(snum, jobid)) + /* + * get the spooled filename of the print job + * if this works, then the file has not been spooled + * to the underlying print system. Just delete the + * spool file & return. + */ + + if ( (fname = print_job_fname( snum, jobid )) != NULL ) + { + /* remove the spool file */ + DEBUG(10,("print_job_delete: Removing spool file [%s]\n", fname )); + if ( unlink( fname ) == -1 ) { + *errcode = map_werror_from_unix(errno); + return False; + } + + return True; + } + + if (!print_job_delete1(snum, jobid)) { + *errcode = WERR_ACCESS_DENIED; return False; + } /* force update the database and say the delete failed if the job still exists */ print_queue_update(snum); + + deleted = !print_job_exists(snum, jobid); + if ( !deleted ) + *errcode = WERR_ACCESS_DENIED; - return !print_job_exists(snum, jobid); + return deleted; } /**************************************************************************** @@ -1161,8 +1353,10 @@ static BOOL print_cache_expired(int snum) DEBUG(3, ("print cache expired for queue %s \ (last_qscan_time = %d, time now = %d, qcachetime = %d)\n", printername, (int)last_qscan_time, (int)time_now, (int)lp_lpqcachetime() )); + release_print_db(pdb); return True; } + release_print_db(pdb); return False; } @@ -1184,6 +1378,7 @@ static int get_queue_status(int snum, print_status_struct *status) key.dptr = keystr; key.dsize = strlen(keystr); data = tdb_fetch(pdb->tdb, key); + release_print_db(pdb); if (data.dptr) { if (data.dsize == sizeof(print_status_struct)) { memcpy(status, data.dptr, sizeof(print_status_struct)); @@ -1214,44 +1409,11 @@ int print_queue_length(int snum, print_status_struct *pstatus) return len; } -/**************************************************************************** - Determine the number of jobs in all queues. This is very expensive. Don't - call ! JRA. -****************************************************************************/ - -static int get_total_jobs(void) -{ - int total_jobs = 0; - int snum; - int services = lp_numservices(); - - for (snum = 0; snum < services; snum++) { - struct tdb_print_db *pdb; - int jobs; - - if (!lp_print_ok(snum)) - continue; - - pdb = get_print_db_byname(lp_const_servicename(snum)); - if (!pdb) - continue; - - /* make sure the database is up to date */ - if (print_cache_expired(snum)) - print_queue_update(snum); - - jobs = tdb_fetch_int32(pdb->tdb, "INFO/total_jobs"); - if (jobs > 0) - total_jobs += jobs; - } - return total_jobs; -} - /*************************************************************************** Start spooling a job - return the jobid. ***************************************************************************/ -uint32 print_job_start(struct current_user *user, int snum, char *jobname) +uint32 print_job_start(struct current_user *user, int snum, char *jobname, NT_DEVICEMODE *nt_devmode ) { uint32 jobid; char *path; @@ -1261,6 +1423,7 @@ uint32 print_job_start(struct current_user *user, int snum, char *jobname) int njobs = 0; const char *printername = lp_const_servicename(snum); struct tdb_print_db *pdb = get_print_db_byname(printername); + BOOL pdb_locked = False; errno = 0; @@ -1269,11 +1432,13 @@ uint32 print_job_start(struct current_user *user, int snum, char *jobname) if (!print_access_check(user, snum, PRINTER_ACCESS_USE)) { DEBUG(3, ("print_job_start: job start denied by security descriptor\n")); + release_print_db(pdb); return (uint32)-1; } if (!print_time_access_check(snum)) { DEBUG(3, ("print_job_start: job start denied by time check\n")); + release_print_db(pdb); return (uint32)-1; } @@ -1285,6 +1450,7 @@ uint32 print_job_start(struct current_user *user, int snum, char *jobname) if (sys_fsusage(path, &dspace, &dsize) == 0 && dspace < 2*(SMB_BIG_UINT)lp_minprintspace(snum)) { DEBUG(3, ("print_job_start: disk space check failed.\n")); + release_print_db(pdb); errno = ENOSPC; return (uint32)-1; } @@ -1293,6 +1459,7 @@ uint32 print_job_start(struct current_user *user, int snum, char *jobname) /* for autoloaded printers, check that the printcap entry still exists */ if (lp_autoloaded(snum) && !pcap_printername_ok(lp_const_servicename(snum), NULL)) { DEBUG(3, ("print_job_start: printer name %s check failed.\n", lp_const_servicename(snum) )); + release_print_db(pdb); errno = ENOENT; return (uint32)-1; } @@ -1301,41 +1468,19 @@ uint32 print_job_start(struct current_user *user, int snum, char *jobname) 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) )); + release_print_db(pdb); errno = ENOSPC; return (uint32)-1; } - /* Insure the maximum print jobs in the system is not violated */ - if (lp_totalprintjobs() && get_total_jobs() > lp_totalprintjobs()) { - DEBUG(3, ("print_job_start: number of jobs (%d) larger than max printjobs per system (%d).\n", - njobs, lp_totalprintjobs() )); - errno = ENOSPC; + /* Lock the database */ + if (tdb_lock_bystring(pdb->tdb, "INFO/nextjob") == -1) { + DEBUG(0,("print_job_start: failed to lock printing database %s\n", printername )); + release_print_db(pdb); return (uint32)-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_const_servicename(snum)); - - /* lock the database */ - tdb_lock_bystring(pdb->tdb, "INFO/nextjob"); + pdb_locked = True; next_jobid = tdb_fetch_int32(pdb->tdb, "INFO/nextjob"); if (next_jobid == -1) @@ -1345,19 +1490,61 @@ uint32 print_job_start(struct current_user *user, int snum, char *jobname) if (!print_job_exists(snum, jobid)) break; } - if (jobid == next_jobid || !pjob_store(snum, jobid, &pjob)) { - DEBUG(3, ("print_job_start: either jobid (%d)==next_jobid(%d) or pjob_store failed.\n", + + if (jobid == next_jobid) { + DEBUG(3, ("print_job_start: jobid (%d)==next_jobid(%d).\n", jobid, next_jobid )); jobid = -1; goto fail; } + /* Store a dummy placeholder. This must be quick as we have the lock. */ + { + TDB_DATA dum; + dum.dptr = NULL; + dum.dsize = 0; + if (tdb_store(pdb->tdb, print_key(jobid), dum, TDB_INSERT) == -1) { + DEBUG(3, ("print_job_start: jobid (%d) failed to store placeholder.\n", + jobid )); + jobid = -1; + goto fail; + } + } + if (tdb_store_int32(pdb->tdb, "INFO/nextjob", jobid)==-1) { DEBUG(3, ("print_job_start: failed to store INFO/nextjob.\n")); jobid = -1; goto fail; } + /* We've finished with the INFO/nextjob lock. */ + tdb_unlock_bystring(pdb->tdb, "INFO/nextjob"); + pdb_locked = False; + + /* 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; + pjob.nt_devmode = nt_devmode; + + 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_const_servicename(snum)); + /* we have a job entry - now create the spool file */ slprintf(pjob.filename, sizeof(pjob.filename)-1, "%s/%s%.8u.XXXXXX", path, PRINT_SPOOL_PREFIX, (unsigned int)jobid); @@ -1378,7 +1565,7 @@ to open spool file %s.\n", pjob.filename)); pjob_store(snum, jobid, &pjob); - tdb_unlock_bystring(pdb->tdb, "INFO/nextjob"); + release_print_db(pdb); /* * If the printer is marked as postscript output a leading @@ -1397,7 +1584,9 @@ to open spool file %s.\n", pjob.filename)); if (jobid != -1) pjob_delete(snum, jobid); - tdb_unlock_bystring(pdb->tdb, "INFO/nextjob"); + if (pdb_locked) + tdb_unlock_bystring(pdb->tdb, "INFO/nextjob"); + release_print_db(pdb); DEBUG(3, ("print_job_start: returning fail. Error = %s\n", strerror(errno) )); return -1; @@ -1503,10 +1692,16 @@ static int traverse_fn_queue(TDB_CONTEXT *t, TDB_DATA key, TDB_DATA data, void * int i; uint32 jobid; - if (data.dsize != sizeof(pjob) || key.dsize != sizeof(int)) + /* sanity checks */ + + if ( key.dsize != sizeof(jobid) ) return 0; + memcpy(&jobid, key.dptr, sizeof(jobid)); - memcpy(&pjob, data.dptr, sizeof(pjob)); + + if ( unpack_pjob( data.dptr, data.dsize, &pjob ) == -1 ) + return 0; + free_nt_devicemode( &pjob.nt_devmode ); /* maybe it isn't for this queue */ if (ts->snum != lp_servicenumber(pjob.queuename)) @@ -1545,10 +1740,17 @@ static int traverse_count_fn_queue(TDB_CONTEXT *t, TDB_DATA key, TDB_DATA data, struct printjob pjob; uint32 jobid; - if (data.dsize != sizeof(pjob) || key.dsize != sizeof(int)) + /* sanity checks */ + + if ( key.dsize != sizeof(jobid) ) return 0; + memcpy(&jobid, key.dptr, sizeof(jobid)); - memcpy(&pjob, data.dptr, sizeof(pjob)); + + if ( unpack_pjob( data.dptr, data.dsize, &pjob ) == -1 ) + return 0; + + free_nt_devicemode( &pjob.nt_devmode ); /* maybe it isn't for this queue - this cannot happen with the tdb/printer code. JRA */ if (ts->snum != lp_servicenumber(pjob.queuename)) @@ -1630,13 +1832,17 @@ int print_queue_status(int snum, tdb_traverse(pdb->tdb, traverse_count_fn_queue, (void *)&tsc); - if (tsc.count == 0) + if (tsc.count == 0) { + release_print_db(pdb); return 0; + } /* Allocate the queue size. */ if ((tstruct.queue = (print_queue_struct *) - malloc(sizeof(print_queue_struct)*tsc.count)) == NULL) + malloc(sizeof(print_queue_struct)*tsc.count)) == NULL) { + release_print_db(pdb); return 0; + } /* * Fill in the queue. @@ -1648,6 +1854,7 @@ int print_queue_status(int snum, tstruct.snum = snum; tdb_traverse(pdb->tdb, traverse_fn_queue, (void *)&tstruct); + release_print_db(pdb); /* Sort the queue by submission time otherwise they are displayed in hash order. */ -- cgit