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 = (char *)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_free_children(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 = (char *)TALLOC(send_ctx, offset); 144 if (!buf) { 145 DEBUG(0,("print_notify_send_messages: Out of memory\n")); 146 talloc_free_children(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_to_procid(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_to_procid(pid_list[i]), 186 MSG_PRINTER_NOTIFY2, 187 buf, offset, True, timeout); 188 } 189} 190 191/******************************************************************* 192 Actually send the batched messages. 193*******************************************************************/ 194 195void print_notify_send_messages(unsigned int timeout) 196{ 197 if (!print_notify_messages_pending()) 198 return; 199 200 if (!create_send_ctx()) 201 return; 202 203 while (print_notify_messages_pending()) 204 print_notify_send_messages_to_printer(notify_queue_head->msg->printer, timeout); 205 206 talloc_free_children(send_ctx); 207 num_messages = 0; 208} 209 210/********************************************************************** 211 deep copy a SPOOLSS_NOTIFY_MSG structure using a TALLOC_CTX 212 *********************************************************************/ 213 214static BOOL copy_notify2_msg( SPOOLSS_NOTIFY_MSG *to, SPOOLSS_NOTIFY_MSG *from ) 215{ 216 217 if ( !to || !from ) 218 return False; 219 220 memcpy( to, from, sizeof(SPOOLSS_NOTIFY_MSG) ); 221 222 if ( from->len ) { 223 to->notify.data = (char *)TALLOC_MEMDUP(send_ctx, from->notify.data, from->len ); 224 if ( !to->notify.data ) { 225 DEBUG(0,("copy_notify2_msg: TALLOC_MEMDUP() of size [%d] failed!\n", from->len )); 226 return False; 227 } 228 } 229 230 231 return True; 232} 233 234/******************************************************************* 235 Batch up print notify messages. 236*******************************************************************/ 237 238static void send_spoolss_notify2_msg(SPOOLSS_NOTIFY_MSG *msg) 239{ 240 struct notify_queue *pnqueue, *tmp_ptr; 241 242 /* 243 * Ensure we only have one job total_bytes and job total_pages for 244 * each job. There is no point in sending multiple messages that match 245 * as they will just cause flickering updates in the client. 246 */ 247 248 if ((num_messages < 100) && (msg->type == JOB_NOTIFY_TYPE) 249 && (msg->field == JOB_NOTIFY_TOTAL_BYTES 250 || msg->field == JOB_NOTIFY_TOTAL_PAGES )) 251 { 252 253 for (tmp_ptr = notify_queue_head; tmp_ptr; tmp_ptr = tmp_ptr->next) 254 { 255 if (tmp_ptr->msg->type == msg->type && 256 tmp_ptr->msg->field == msg->field && 257 tmp_ptr->msg->id == msg->id && 258 tmp_ptr->msg->flags == msg->flags && 259 strequal(tmp_ptr->msg->printer, msg->printer)) { 260 261 DEBUG(5,("send_spoolss_notify2_msg: replacing message 0x%02x/0x%02x for " 262 "printer %s in notify_queue\n", msg->type, msg->field, msg->printer)); 263 264 tmp_ptr->msg = msg; 265 return; 266 } 267 } 268 } 269 270 /* Store the message on the pending queue. */ 271 272 pnqueue = TALLOC_P(send_ctx, struct notify_queue); 273 if (!pnqueue) { 274 DEBUG(0,("send_spoolss_notify2_msg: Out of memory.\n")); 275 return; 276 } 277 278 /* allocate a new msg structure and copy the fields */ 279 280 if ( !(pnqueue->msg = TALLOC_P(send_ctx, SPOOLSS_NOTIFY_MSG)) ) { 281 DEBUG(0,("send_spoolss_notify2_msg: talloc() of size [%lu] failed!\n", 282 (unsigned long)sizeof(SPOOLSS_NOTIFY_MSG))); 283 return; 284 } 285 copy_notify2_msg(pnqueue->msg, msg); 286 GetTimeOfDay(&pnqueue->tv); 287 pnqueue->buf = NULL; 288 pnqueue->buflen = 0; 289 290 DEBUG(5, ("send_spoolss_notify2_msg: appending message 0x%02x/0x%02x for printer %s \ 291to notify_queue_head\n", msg->type, msg->field, msg->printer)); 292 293 /* 294 * Note we add to the end of the list to ensure 295 * the messages are sent in the order they were received. JRA. 296 */ 297 298 DLIST_ADD_END(notify_queue_head, pnqueue, struct notify_queue *); 299 num_messages++; 300} 301 302static void send_notify_field_values(const char *sharename, uint32 type, 303 uint32 field, uint32 id, uint32 value1, 304 uint32 value2, uint32 flags) 305{ 306 struct spoolss_notify_msg *msg; 307 308 if (lp_disable_spoolss()) 309 return; 310 311 if (!create_send_ctx()) 312 return; 313 314 msg = TALLOC_P(send_ctx, struct spoolss_notify_msg); 315 if (!msg) 316 return; 317 318 ZERO_STRUCTP(msg); 319 320 fstrcpy(msg->printer, sharename); 321 msg->type = type; 322 msg->field = field; 323 msg->id = id; 324 msg->notify.value[0] = value1; 325 msg->notify.value[1] = value2; 326 msg->flags = flags; 327 328 send_spoolss_notify2_msg(msg); 329} 330 331static void send_notify_field_buffer(const char *sharename, uint32 type, 332 uint32 field, uint32 id, uint32 len, 333 const char *buffer) 334{ 335 struct spoolss_notify_msg *msg; 336 337 if (lp_disable_spoolss()) 338 return; 339 340 if (!create_send_ctx()) 341 return; 342 343 msg = TALLOC_P(send_ctx, struct spoolss_notify_msg); 344 if (!msg) 345 return; 346 347 ZERO_STRUCTP(msg); 348 349 fstrcpy(msg->printer, sharename); 350 msg->type = type; 351 msg->field = field; 352 msg->id = id; 353 msg->len = len; 354 msg->notify.data = CONST_DISCARD(char *,buffer); 355 356 send_spoolss_notify2_msg(msg); 357} 358 359/* Send a message that the printer status has changed */ 360 361void notify_printer_status_byname(const char *sharename, uint32 status) 362{ 363 /* Printer status stored in value1 */ 364 365 send_notify_field_values(sharename, PRINTER_NOTIFY_TYPE, 366 PRINTER_NOTIFY_STATUS, 0, 367 status, 0, 0); 368} 369 370void notify_printer_status(int snum, uint32 status) 371{ 372 const char *sharename = SERVICE(snum); 373 374 if (sharename) 375 notify_printer_status_byname(sharename, status); 376} 377 378void notify_job_status_byname(const char *sharename, uint32 jobid, uint32 status, 379 uint32 flags) 380{ 381 /* Job id stored in id field, status in value1 */ 382 383 send_notify_field_values(sharename, JOB_NOTIFY_TYPE, 384 JOB_NOTIFY_STATUS, jobid, 385 status, 0, flags); 386} 387 388void notify_job_status(const char *sharename, uint32 jobid, uint32 status) 389{ 390 notify_job_status_byname(sharename, jobid, status, 0); 391} 392 393void notify_job_total_bytes(const char *sharename, uint32 jobid, 394 uint32 size) 395{ 396 /* Job id stored in id field, status in value1 */ 397 398 send_notify_field_values(sharename, JOB_NOTIFY_TYPE, 399 JOB_NOTIFY_TOTAL_BYTES, jobid, 400 size, 0, 0); 401} 402 403void notify_job_total_pages(const char *sharename, uint32 jobid, 404 uint32 pages) 405{ 406 /* Job id stored in id field, status in value1 */ 407 408 send_notify_field_values(sharename, JOB_NOTIFY_TYPE, 409 JOB_NOTIFY_TOTAL_PAGES, jobid, 410 pages, 0, 0); 411} 412 413void notify_job_username(const char *sharename, uint32 jobid, char *name) 414{ 415 send_notify_field_buffer( 416 sharename, JOB_NOTIFY_TYPE, JOB_NOTIFY_USER_NAME, 417 jobid, strlen(name) + 1, name); 418} 419 420void notify_job_name(const char *sharename, uint32 jobid, char *name) 421{ 422 send_notify_field_buffer( 423 sharename, JOB_NOTIFY_TYPE, JOB_NOTIFY_DOCUMENT, 424 jobid, strlen(name) + 1, name); 425} 426 427void notify_job_submitted(const char *sharename, uint32 jobid, 428 time_t submitted) 429{ 430 send_notify_field_buffer( 431 sharename, JOB_NOTIFY_TYPE, JOB_NOTIFY_SUBMITTED, 432 jobid, sizeof(submitted), (char *)&submitted); 433} 434 435void notify_printer_driver(int snum, char *driver_name) 436{ 437 const char *sharename = SERVICE(snum); 438 439 send_notify_field_buffer( 440 sharename, PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_DRIVER_NAME, 441 snum, strlen(driver_name) + 1, driver_name); 442} 443 444void notify_printer_comment(int snum, char *comment) 445{ 446 const char *sharename = SERVICE(snum); 447 448 send_notify_field_buffer( 449 sharename, PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_COMMENT, 450 snum, strlen(comment) + 1, comment); 451} 452 453void notify_printer_sharename(int snum, char *share_name) 454{ 455 const char *sharename = SERVICE(snum); 456 457 send_notify_field_buffer( 458 sharename, PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_SHARE_NAME, 459 snum, strlen(share_name) + 1, share_name); 460} 461 462void notify_printer_printername(int snum, char *printername) 463{ 464 const char *sharename = SERVICE(snum); 465 466 send_notify_field_buffer( 467 sharename, PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_PRINTER_NAME, 468 snum, strlen(printername) + 1, printername); 469} 470 471void notify_printer_port(int snum, char *port_name) 472{ 473 const char *sharename = SERVICE(snum); 474 475 send_notify_field_buffer( 476 sharename, PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_PORT_NAME, 477 snum, strlen(port_name) + 1, port_name); 478} 479 480void notify_printer_location(int snum, char *location) 481{ 482 const char *sharename = SERVICE(snum); 483 484 send_notify_field_buffer( 485 sharename, PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_LOCATION, 486 snum, strlen(location) + 1, location); 487} 488 489void notify_printer_byname( const char *printername, uint32 change, const char *value ) 490{ 491 int snum = print_queue_snum(printername); 492 int type = PRINTER_NOTIFY_TYPE; 493 494 if ( snum == -1 ) 495 return; 496 497 send_notify_field_buffer( printername, type, change, snum, strlen(value)+1, value ); 498} 499 500 501/**************************************************************************** 502 Return a malloced list of pid_t's that are interested in getting update 503 messages on this print queue. Used in printing/notify to send the messages. 504****************************************************************************/ 505 506BOOL print_notify_pid_list(const char *printername, TALLOC_CTX *mem_ctx, size_t *p_num_pids, pid_t **pp_pid_list) 507{ 508 struct tdb_print_db *pdb = NULL; 509 TDB_CONTEXT *tdb = NULL; 510 TDB_DATA data; 511 BOOL ret = True; 512 size_t i, num_pids, offset; 513 pid_t *pid_list; 514 515 *p_num_pids = 0; 516 *pp_pid_list = NULL; 517 518 pdb = get_print_db_byname(printername); 519 if (!pdb) 520 return False; 521 tdb = pdb->tdb; 522 523 if (tdb_read_lock_bystring_with_timeout(tdb, NOTIFY_PID_LIST_KEY, 10) == -1) { 524 DEBUG(0,("print_notify_pid_list: Failed to lock printer %s database\n", 525 printername)); 526 if (pdb) 527 release_print_db(pdb); 528 return False; 529 } 530 531 data = get_printer_notify_pid_list( tdb, printername, True ); 532 533 if (!data.dptr) { 534 ret = True; 535 goto done; 536 } 537 538 num_pids = data.dsize / 8; 539 540 if (num_pids) { 541 if ((pid_list = TALLOC_ARRAY(mem_ctx, pid_t, num_pids)) == NULL) { 542 ret = False; 543 goto done; 544 } 545 } else { 546 pid_list = NULL; 547 } 548 549 for( i = 0, offset = 0; offset < data.dsize; offset += 8, i++) 550 pid_list[i] = (pid_t)IVAL(data.dptr, offset); 551 552 *pp_pid_list = pid_list; 553 *p_num_pids = num_pids; 554 555 ret = True; 556 557 done: 558 559 tdb_read_unlock_bystring(tdb, NOTIFY_PID_LIST_KEY); 560 if (pdb) 561 release_print_db(pdb); 562 SAFE_FREE(data.dptr); 563 return ret; 564} 565