1/* 2 Unix SMB/Netbios implementation. 3 Version 3.0 4 printing backend routines 5 Copyright (C) Tim Potter, 2002 6 Copyright (C) Gerald Carter, 2002 7 8 This program is free software; you can redistribute it and/or modify 9 it under the terms of the GNU General Public License as published by 10 the Free Software Foundation; either version 2 of the License, or 11 (at your option) any later version. 12 13 This program is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU General Public License for more details. 17 18 You should have received a copy of the GNU General Public License 19 along with this program; if not, write to the Free Software 20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 21*/ 22 23#include "includes.h" 24#include "printing.h" 25 26static TALLOC_CTX *send_ctx; 27 28static unsigned int num_messages; 29 30static struct notify_queue { 31 struct notify_queue *next, *prev; 32 struct spoolss_notify_msg *msg; 33 struct timeval tv; 34 char *buf; 35 size_t buflen; 36} *notify_queue_head = NULL; 37 38 39static BOOL create_send_ctx(void) 40{ 41 if (!send_ctx) 42 send_ctx = talloc_init("print notify queue"); 43 44 if (!send_ctx) 45 return False; 46 47 return True; 48} 49 50/**************************************************************************** 51 Turn a queue name into a snum. 52****************************************************************************/ 53 54int print_queue_snum(const char *qname) 55{ 56 int snum = lp_servicenumber(qname); 57 if (snum == -1 || !lp_print_ok(snum)) 58 return -1; 59 return snum; 60} 61 62/******************************************************************* 63 Used to decide if we need a short select timeout. 64*******************************************************************/ 65 66BOOL print_notify_messages_pending(void) 67{ 68 return (notify_queue_head != NULL); 69} 70 71/******************************************************************* 72 Flatten data into a message. 73*******************************************************************/ 74 75static BOOL flatten_message(struct notify_queue *q) 76{ 77 struct spoolss_notify_msg *msg = q->msg; 78 char *buf = NULL; 79 size_t buflen = 0, len; 80 81again: 82 len = 0; 83 84 /* Pack header */ 85 86 len += tdb_pack(buf + len, buflen - len, "f", msg->printer); 87 88 len += tdb_pack(buf + len, buflen - len, "ddddddd", 89 (uint32)q->tv.tv_sec, (uint32)q->tv.tv_usec, 90 msg->type, msg->field, msg->id, msg->len, msg->flags); 91 92 /* Pack data */ 93 94 if (msg->len == 0) 95 len += tdb_pack(buf + len, buflen - len, "dd", 96 msg->notify.value[0], msg->notify.value[1]); 97 else 98 len += tdb_pack(buf + len, buflen - len, "B", 99 msg->len, msg->notify.data); 100 101 if (buflen != len) { 102 buf = talloc_realloc(send_ctx, buf, len); 103 if (!buf) 104 return False; 105 buflen = len; 106 goto again; 107 } 108 109 q->buf = buf; 110 q->buflen = buflen; 111 112 return True; 113} 114 115/******************************************************************* 116 Send the batched messages - on a per-printer basis. 117*******************************************************************/ 118 119static void print_notify_send_messages_to_printer(const char *printer, unsigned int timeout) 120{ 121 char *buf; 122 struct notify_queue *pq, *pq_next; 123 size_t msg_count = 0, offset = 0; 124 size_t num_pids = 0; 125 size_t i; 126 pid_t *pid_list = NULL; 127 128 /* Count the space needed to send the messages. */ 129 for (pq = notify_queue_head; pq; pq = pq->next) { 130 if (strequal(printer, pq->msg->printer)) { 131 if (!flatten_message(pq)) { 132 DEBUG(0,("print_notify_send_messages: Out of memory\n")); 133 talloc_destroy_pool(send_ctx); 134 num_messages = 0; 135 return; 136 } 137 offset += (pq->buflen + 4); 138 msg_count++; 139 } 140 } 141 offset += 4; /* For count. */ 142 143 buf = talloc(send_ctx, offset); 144 if (!buf) { 145 DEBUG(0,("print_notify_send_messages: Out of memory\n")); 146 talloc_destroy_pool(send_ctx); 147 num_messages = 0; 148 return; 149 } 150 151 offset = 0; 152 SIVAL(buf,offset,msg_count); 153 offset += 4; 154 for (pq = notify_queue_head; pq; pq = pq_next) { 155 pq_next = pq->next; 156 157 if (strequal(printer, pq->msg->printer)) { 158 SIVAL(buf,offset,pq->buflen); 159 offset += 4; 160 memcpy(buf + offset, pq->buf, pq->buflen); 161 offset += pq->buflen; 162 163 /* Remove from list. */ 164 DLIST_REMOVE(notify_queue_head, pq); 165 } 166 } 167 168 DEBUG(5, ("print_notify_send_messages_to_printer: sending %lu print notify message%s to printer %s\n", 169 (unsigned long)msg_count, msg_count != 1 ? "s" : "", printer)); 170 171 /* 172 * Get the list of PID's to send to. 173 */ 174 175 if (!print_notify_pid_list(printer, send_ctx, &num_pids, &pid_list)) 176 return; 177 178 for (i = 0; i < num_pids; i++) { 179 unsigned int q_len = messages_pending_for_pid(pid_list[i]); 180 if (q_len > 1000) { 181 DEBUG(5, ("print_notify_send_messages_to_printer: discarding notify to printer %s as queue length = %u\n", 182 printer, q_len )); 183 continue; 184 } 185 message_send_pid_with_timeout(pid_list[i], MSG_PRINTER_NOTIFY2, buf, offset, True, timeout); 186 } 187} 188 189/******************************************************************* 190 Actually send the batched messages. 191*******************************************************************/ 192 193void print_notify_send_messages(unsigned int timeout) 194{ 195 if (!print_notify_messages_pending()) 196 return; 197 198 if (!create_send_ctx()) 199 return; 200 201 while (print_notify_messages_pending()) 202 print_notify_send_messages_to_printer(notify_queue_head->msg->printer, timeout); 203 204 talloc_destroy_pool(send_ctx); 205 num_messages = 0; 206} 207 208/********************************************************************** 209 deep copy a SPOOLSS_NOTIFY_MSG structure using a TALLOC_CTX 210 *********************************************************************/ 211 212static BOOL copy_notify2_msg( SPOOLSS_NOTIFY_MSG *to, SPOOLSS_NOTIFY_MSG *from ) 213{ 214 215 if ( !to || !from ) 216 return False; 217 218 memcpy( to, from, sizeof(SPOOLSS_NOTIFY_MSG) ); 219 220 if ( from->len ) { 221 to->notify.data = talloc_memdup(send_ctx, from->notify.data, from->len ); 222 if ( !to->notify.data ) { 223 DEBUG(0,("copy_notify2_msg: talloc_memdup() of size [%d] failed!\n", from->len )); 224 return False; 225 } 226 } 227 228 229 return True; 230} 231 232/******************************************************************* 233 Batch up print notify messages. 234*******************************************************************/ 235 236static void send_spoolss_notify2_msg(SPOOLSS_NOTIFY_MSG *msg) 237{ 238 struct notify_queue *pnqueue, *tmp_ptr; 239 240 /* 241 * Ensure we only have one job total_bytes and job total_pages for 242 * each job. There is no point in sending multiple messages that match 243 * as they will just cause flickering updates in the client. 244 */ 245 246 if ((num_messages < 100) && (msg->type == JOB_NOTIFY_TYPE) && 247 (msg->field == JOB_NOTIFY_TOTAL_BYTES || msg->field == JOB_NOTIFY_TOTAL_PAGES)) { 248 249 for (tmp_ptr = notify_queue_head; tmp_ptr; tmp_ptr = tmp_ptr->next) { 250 if (tmp_ptr->msg->type == msg->type && 251 tmp_ptr->msg->field == msg->field && 252 tmp_ptr->msg->id == msg->id && 253 tmp_ptr->msg->flags == msg->flags && 254 strequal(tmp_ptr->msg->printer, msg->printer)) { 255 256 DEBUG(5, ("send_spoolss_notify2_msg: replacing message 0x%02x/0x%02x for printer %s \ 257in notify_queue\n", msg->type, msg->field, msg->printer)); 258 259 tmp_ptr->msg = msg; 260 return; 261 } 262 } 263 } 264 265 /* Store the message on the pending queue. */ 266 267 pnqueue = talloc(send_ctx, sizeof(*pnqueue)); 268 if (!pnqueue) { 269 DEBUG(0,("send_spoolss_notify2_msg: Out of memory.\n")); 270 return; 271 } 272 273 /* allocate a new msg structure and copy the fields */ 274 275 if ( !(pnqueue->msg = (SPOOLSS_NOTIFY_MSG*)talloc(send_ctx, sizeof(SPOOLSS_NOTIFY_MSG))) ) { 276 DEBUG(0,("send_spoolss_notify2_msg: talloc() of size [%lu] failed!\n", 277 (unsigned long)sizeof(SPOOLSS_NOTIFY_MSG))); 278 return; 279 } 280 copy_notify2_msg(pnqueue->msg, msg); 281 gettimeofday(&pnqueue->tv, NULL); 282 pnqueue->buf = NULL; 283 pnqueue->buflen = 0; 284 285 DEBUG(5, ("send_spoolss_notify2_msg: appending message 0x%02x/0x%02x for printer %s \ 286to notify_queue_head\n", msg->type, msg->field, msg->printer)); 287 288 /* 289 * Note we add to the end of the list to ensure 290 * the messages are sent in the order they were received. JRA. 291 */ 292 293 DLIST_ADD_END(notify_queue_head, pnqueue, tmp_ptr); 294 num_messages++; 295} 296 297static void send_notify_field_values(const char *printer_name, uint32 type, 298 uint32 field, uint32 id, uint32 value1, 299 uint32 value2, uint32 flags) 300{ 301 struct spoolss_notify_msg *msg; 302 303 if (lp_disable_spoolss()) 304 return; 305 306 if (!create_send_ctx()) 307 return; 308 309 msg = (struct spoolss_notify_msg *)talloc(send_ctx, sizeof(struct spoolss_notify_msg)); 310 if (!msg) 311 return; 312 313 ZERO_STRUCTP(msg); 314 315 fstrcpy(msg->printer, printer_name); 316 msg->type = type; 317 msg->field = field; 318 msg->id = id; 319 msg->notify.value[0] = value1; 320 msg->notify.value[1] = value2; 321 msg->flags = flags; 322 323 send_spoolss_notify2_msg(msg); 324} 325 326static void send_notify_field_buffer(const char *printer_name, uint32 type, 327 uint32 field, uint32 id, uint32 len, 328 char *buffer) 329{ 330 struct spoolss_notify_msg *msg; 331 332 if (lp_disable_spoolss()) 333 return; 334 335 if (!create_send_ctx()) 336 return; 337 338 msg = (struct spoolss_notify_msg *)talloc(send_ctx, sizeof(struct spoolss_notify_msg)); 339 if (!msg) 340 return; 341 342 ZERO_STRUCTP(msg); 343 344 fstrcpy(msg->printer, printer_name); 345 msg->type = type; 346 msg->field = field; 347 msg->id = id; 348 msg->len = len; 349 msg->notify.data = buffer; 350 351 send_spoolss_notify2_msg(msg); 352} 353 354/* Send a message that the printer status has changed */ 355 356void notify_printer_status_byname(const char *printer_name, uint32 status) 357{ 358 /* Printer status stored in value1 */ 359 360 send_notify_field_values(printer_name, PRINTER_NOTIFY_TYPE, 361 PRINTER_NOTIFY_STATUS, 0, 362 status, 0, 0); 363} 364 365void notify_printer_status(int snum, uint32 status) 366{ 367 const char *printer_name = SERVICE(snum); 368 369 if (printer_name) 370 notify_printer_status_byname(printer_name, status); 371} 372 373void notify_job_status_byname(const char *printer_name, uint32 jobid, uint32 status, 374 uint32 flags) 375{ 376 /* Job id stored in id field, status in value1 */ 377 378 send_notify_field_values(printer_name, JOB_NOTIFY_TYPE, 379 JOB_NOTIFY_STATUS, jobid, 380 status, 0, flags); 381} 382 383void notify_job_status(int snum, uint32 jobid, uint32 status) 384{ 385 const char *printer_name = SERVICE(snum); 386 387 notify_job_status_byname(printer_name, jobid, status, 0); 388} 389 390void notify_job_total_bytes(int snum, uint32 jobid, uint32 size) 391{ 392 const char *printer_name = SERVICE(snum); 393 394 /* Job id stored in id field, status in value1 */ 395 396 send_notify_field_values(printer_name, JOB_NOTIFY_TYPE, 397 JOB_NOTIFY_TOTAL_BYTES, jobid, 398 size, 0, 0); 399} 400 401void notify_job_total_pages(int snum, uint32 jobid, uint32 pages) 402{ 403 const char *printer_name = SERVICE(snum); 404 405 /* Job id stored in id field, status in value1 */ 406 407 send_notify_field_values(printer_name, JOB_NOTIFY_TYPE, 408 JOB_NOTIFY_TOTAL_PAGES, jobid, 409 pages, 0, 0); 410} 411 412void notify_job_username(int snum, uint32 jobid, char *name) 413{ 414 const char *printer_name = SERVICE(snum); 415 416 send_notify_field_buffer( 417 printer_name, JOB_NOTIFY_TYPE, JOB_NOTIFY_USER_NAME, 418 jobid, strlen(name) + 1, name); 419} 420 421void notify_job_name(int snum, uint32 jobid, char *name) 422{ 423 const char *printer_name = SERVICE(snum); 424 425 send_notify_field_buffer( 426 printer_name, JOB_NOTIFY_TYPE, JOB_NOTIFY_DOCUMENT, 427 jobid, strlen(name) + 1, name); 428} 429 430void notify_job_submitted(int snum, uint32 jobid, time_t submitted) 431{ 432 const char *printer_name = SERVICE(snum); 433 434 send_notify_field_buffer( 435 printer_name, JOB_NOTIFY_TYPE, JOB_NOTIFY_SUBMITTED, 436 jobid, sizeof(submitted), (char *)&submitted); 437} 438 439void notify_printer_driver(int snum, char *driver_name) 440{ 441 const char *printer_name = SERVICE(snum); 442 443 send_notify_field_buffer( 444 printer_name, PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_DRIVER_NAME, 445 snum, strlen(driver_name) + 1, driver_name); 446} 447 448void notify_printer_comment(int snum, char *comment) 449{ 450 const char *printer_name = SERVICE(snum); 451 452 send_notify_field_buffer( 453 printer_name, PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_COMMENT, 454 snum, strlen(comment) + 1, comment); 455} 456 457void notify_printer_sharename(int snum, char *share_name) 458{ 459 const char *printer_name = SERVICE(snum); 460 461 send_notify_field_buffer( 462 printer_name, PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_SHARE_NAME, 463 snum, strlen(share_name) + 1, share_name); 464} 465 466void notify_printer_port(int snum, char *port_name) 467{ 468 const char *printer_name = SERVICE(snum); 469 470 send_notify_field_buffer( 471 printer_name, PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_PORT_NAME, 472 snum, strlen(port_name) + 1, port_name); 473} 474 475void notify_printer_location(int snum, char *location) 476{ 477 const char *printer_name = SERVICE(snum); 478 479 send_notify_field_buffer( 480 printer_name, PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_LOCATION, 481 snum, strlen(location) + 1, location); 482} 483 484void notify_printer_byname( const char *printername, uint32 change, char *value ) 485{ 486 int snum = print_queue_snum(printername); 487 int type = PRINTER_NOTIFY_TYPE; 488 489 if ( snum == -1 ) 490 return; 491 492 send_notify_field_buffer( printername, type, change, snum, strlen(value)+1, value ); 493} 494 495 496/**************************************************************************** 497 Return a malloced list of pid_t's that are interested in getting update 498 messages on this print queue. Used in printing/notify to send the messages. 499****************************************************************************/ 500 501BOOL print_notify_pid_list(const char *printername, TALLOC_CTX *mem_ctx, size_t *p_num_pids, pid_t **pp_pid_list) 502{ 503 struct tdb_print_db *pdb = NULL; 504 TDB_CONTEXT *tdb = NULL; 505 TDB_DATA data; 506 BOOL ret = True; 507 size_t i, num_pids, offset; 508 pid_t *pid_list; 509 510 *p_num_pids = 0; 511 *pp_pid_list = NULL; 512 513 pdb = get_print_db_byname(printername); 514 if (!pdb) 515 return False; 516 tdb = pdb->tdb; 517 518 if (tdb_read_lock_bystring(tdb, NOTIFY_PID_LIST_KEY, 10) == -1) { 519 DEBUG(0,("print_notify_pid_list: Failed to lock printer %s database\n", 520 printername)); 521 if (pdb) 522 release_print_db(pdb); 523 return False; 524 } 525 526 data = get_printer_notify_pid_list( tdb, printername, True ); 527 528 if (!data.dptr) { 529 ret = True; 530 goto done; 531 } 532 533 num_pids = data.dsize / 8; 534 535 if ((pid_list = (pid_t *)talloc(mem_ctx, sizeof(pid_t) * num_pids)) == NULL) { 536 ret = False; 537 goto done; 538 } 539 540 for( i = 0, offset = 0; offset < data.dsize; offset += 8, i++) 541 pid_list[i] = (pid_t)IVAL(data.dptr, offset); 542 543 *pp_pid_list = pid_list; 544 *p_num_pids = num_pids; 545 546 ret = True; 547 548 done: 549 550 tdb_read_unlock_bystring(tdb, NOTIFY_PID_LIST_KEY); 551 if (pdb) 552 release_print_db(pdb); 553 SAFE_FREE(data.dptr); 554 return ret; 555} 556