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