If you have a large number of cups printers, then scanning for print info can cause...
authorJeremy Allison <jra@samba.org>
Fri, 10 Oct 2008 18:55:14 +0000 (11:55 -0700)
committerJeremy Allison <jra@samba.org>
Fri, 10 Oct 2008 18:55:14 +0000 (11:55 -0700)
(it takes longer than 30 seconds to enumerate them). Make scanning for printers async with a callback
from the main loop. This fixes a bug that was irritating *me* :-).
Jeremy.

source3/include/proto.h
source3/include/smb.h
source3/param/loadparm.c
source3/printing/load.c
source3/printing/pcap.c
source3/printing/print_cups.c

index b7a7ed547930836f368fa7c196ac7d9fb4977bd3..fc497b69661c71857968e5f64f0c380547a5acd0 100644 (file)
@@ -5995,7 +5995,7 @@ bool dump_a_parameter(int snum, char *parm_name, FILE * f, bool isGlobal);
 struct parm_struct *lp_get_parameter(const char *param_name);
 struct parm_struct *lp_next_parameter(int snum, int *i, int allparameters);
 bool lp_snum_ok(int iService);
-void lp_add_one_printer(char *name, char *comment);
+void lp_add_one_printer(const char *name, const char *comment, void *pdata);
 bool lp_loaded(void);
 void lp_killunused(bool (*snumused) (int));
 void lp_kill_all_services(void);
@@ -6568,11 +6568,15 @@ char* get_server_name( Printer_entry *printer );
 
 /* The following definitions come from printing/pcap.c  */
 
+bool pcap_cache_add_specific(struct pcap_cache **ppcache, const char *name, const char *comment);
+void pcap_cache_destroy_specific(struct pcap_cache **ppcache);
 bool pcap_cache_add(const char *name, const char *comment);
 bool pcap_cache_loaded(void);
+void pcap_cache_replace(const struct pcap_cache *cache);
 void pcap_cache_reload(void);
 bool pcap_printername_ok(const char *printername);
-void pcap_printer_fn(void (*fn)(char *, char *));
+void pcap_printer_fn_specific(const struct pcap_cache *, void (*fn)(const char *, const char *, void *), void *);
+void pcap_printer_fn(void (*fn)(const char *, const char *, void *), void *);
 
 /* The following definitions come from printing/print_aix.c  */
 
index c8c4f8c3cc98453750b1189e914e0cf7ec433a10..ef98b5e1b545bc3b52635d2b90f82694d95352e5 100644 (file)
@@ -391,6 +391,7 @@ struct idle_event;
 struct share_mode_entry;
 struct uuid;
 struct named_mutex;
+struct pcap_cache;
 
 struct vfs_fsp_data {
     struct vfs_fsp_data *next;
index 94660317f8513033a1bf4ce36cda6194270ab853..1191c3d3aa690905a0a7b11f79d0ebb61b0cc174 100644 (file)
@@ -7848,7 +7848,7 @@ static void lp_add_auto_services(char *str)
  Auto-load one printer.
 ***************************************************************************/
 
-void lp_add_one_printer(char *name, char *comment)
+void lp_add_one_printer(const char *name, const char *comment, void *pdata)
 {
        int printers = lp_servicenumber(PRINTERS_NAME);
        int i;
index 23144d5a9550dc9e6ba2bea5eaf94d6bccaf4427..fc21f271bd9aa2aaba391edcbeaf31e76d27cbf1 100644 (file)
@@ -60,5 +60,5 @@ void load_printers(void)
 
        /* load all printcap printers */
        if (lp_load_printers() && lp_servicenumber(PRINTERS_NAME) >= 0)
-               pcap_printer_fn(lp_add_one_printer);
+               pcap_printer_fn(lp_add_one_printer, NULL);
 }
index 30cb254a29a1805925f8ff531148a2893500af2d..10c1a2d608bf8df9ea8173440b11707ef2b3a258 100644 (file)
 #include "includes.h"
 
 
-typedef struct pcap_cache {
+struct pcap_cache {
        char *name;
        char *comment;
        struct pcap_cache *next;
-} pcap_cache_t;
+};
 
-static pcap_cache_t *pcap_cache = NULL;
+/* The systemwide printcap cache. */
+static struct pcap_cache *pcap_cache = NULL;
 
-bool pcap_cache_add(const char *name, const char *comment)
+bool pcap_cache_add_specific(struct pcap_cache **ppcache, const char *name, const char *comment)
 {
-       pcap_cache_t *p;
+       struct pcap_cache *p;
 
-       if (name == NULL || ((p = SMB_MALLOC_P(pcap_cache_t)) == NULL))
-               return False;
+       if (name == NULL || ((p = SMB_MALLOC_P(struct pcap_cache)) == NULL))
+               return false;
 
        p->name = SMB_STRDUP(name);
        p->comment = (comment && *comment) ? SMB_STRDUP(comment) : NULL;
 
-       p->next = pcap_cache;
-       pcap_cache = p;
+       DEBUG(11,("pcap_cache_add_specific: Adding name %s info %s\n",
+               p->name, p->comment ? p->comment : ""));
+
+       p->next = *ppcache;
+       *ppcache = p;
 
-       return True;
+       return true;
 }
 
-static void pcap_cache_destroy(pcap_cache_t *cache)
+void pcap_cache_destroy_specific(struct pcap_cache **pp_cache)
 {
-       pcap_cache_t *p, *next;
+       struct pcap_cache *p, *next;
 
-       for (p = cache; p != NULL; p = next) {
+       for (p = *pp_cache; p != NULL; p = next) {
                next = p->next;
 
                SAFE_FREE(p->name);
                SAFE_FREE(p->comment);
                SAFE_FREE(p);
        }
+       *pp_cache = NULL;
+}
+
+bool pcap_cache_add(const char *name, const char *comment)
+{
+       return pcap_cache_add_specific(&pcap_cache, name, comment);
 }
 
 bool pcap_cache_loaded(void)
@@ -105,11 +115,21 @@ bool pcap_cache_loaded(void)
        return (pcap_cache != NULL);
 }
 
+void pcap_cache_replace(const struct pcap_cache *pcache)
+{
+       const struct pcap_cache *p;
+
+       pcap_cache_destroy_specific(&pcap_cache);
+       for (p = pcache; p; p = p->next) {
+               pcap_cache_add(p->name, p->comment);
+       }
+}
+
 void pcap_cache_reload(void)
 {
        const char *pcap_name = lp_printcapname();
        bool pcap_reloaded = False;
-       pcap_cache_t *tmp_cache = NULL;
+       struct pcap_cache *tmp_cache = NULL;
        XFILE *pcap_file;
        char *pcap_line;
 
@@ -223,9 +243,9 @@ done:
        DEBUG(3, ("reload status: %s\n", (pcap_reloaded) ? "ok" : "error"));
 
        if (pcap_reloaded)
-               pcap_cache_destroy(tmp_cache);
+               pcap_cache_destroy_specific(&tmp_cache);
        else {
-               pcap_cache_destroy(pcap_cache);
+               pcap_cache_destroy_specific(&pcap_cache);
                pcap_cache = tmp_cache;
        }
 
@@ -235,7 +255,7 @@ done:
 
 bool pcap_printername_ok(const char *printername)
 {
-       pcap_cache_t *p;
+       struct pcap_cache *p;
 
        for (p = pcap_cache; p != NULL; p = p->next)
                if (strequal(p->name, printername))
@@ -245,19 +265,22 @@ bool pcap_printername_ok(const char *printername)
 }
 
 /***************************************************************************
-run a function on each printer name in the printcap file. The function is 
-passed the primary name and the comment (if possible). Note the fn() takes
-strings in DOS codepage. This means the xxx_printer_fn() calls must be fixed
-to return DOS codepage. FIXME !! JRA.
-
-XXX: I'm not sure if this comment still applies.. Anyone?  -Rob
+run a function on each printer name in the printcap file.
 ***************************************************************************/
-void pcap_printer_fn(void (*fn)(char *, char *))
+
+void pcap_printer_fn_specific(const struct pcap_cache *pc,
+                       void (*fn)(const char *, const char *, void *),
+                       void *pdata)
 {
-       pcap_cache_t *p;
+       const struct pcap_cache *p;
 
-       for (p = pcap_cache; p != NULL; p = p->next)
-               fn(p->name, p->comment);
+       for (p = pc; p != NULL; p = p->next)
+               fn(p->name, p->comment, pdata);
 
        return;
 }
+
+void pcap_printer_fn(void (*fn)(const char *, const char *, void *), void *pdata)
+{
+       return pcap_printer_fn_specific(pcap_cache, fn, pdata);
+}
index b9bed7a138c7d1eebd8a5313dd59b06b009fe0ea..6fe24e181efe69ace5ced17906584abccd2ab9f1 100644 (file)
@@ -106,9 +106,46 @@ static http_t *cups_connect(TALLOC_CTX *frame)
        return http;
 }
 
-bool cups_cache_reload(void)
+static void send_pcap_info(const char *name, const char *info, void *pd)
+{
+       int fd = *(int *)pd;
+       size_t namelen = name ? strlen(name)+1 : 0;
+       size_t infolen = info ? strlen(info)+1 : 0;
+
+       DEBUG(11,("send_pcap_info: writing namelen %u\n", (unsigned int)namelen));
+       if (sys_write(fd, &namelen, sizeof(namelen)) != sizeof(namelen)) {
+               DEBUG(10,("send_pcap_info: namelen write failed %s\n",
+                       strerror(errno)));
+               return;
+       }
+       DEBUG(11,("send_pcap_info: writing infolen %u\n", (unsigned int)infolen));
+       if (sys_write(fd, &infolen, sizeof(infolen)) != sizeof(infolen)) {
+               DEBUG(10,("send_pcap_info: infolen write failed %s\n",
+                       strerror(errno)));
+               return;
+       }
+       if (namelen) {
+               DEBUG(11,("send_pcap_info: writing name %s\n", name));
+               if (sys_write(fd, name, namelen) != namelen) {
+                       DEBUG(10,("send_pcap_info: name write failed %s\n",
+                               strerror(errno)));
+                       return;
+               }
+       }
+       if (infolen) {
+               DEBUG(11,("send_pcap_info: writing info %s\n", info));
+               if (sys_write(fd, info, infolen) != infolen) {
+                       DEBUG(10,("send_pcap_info: info write failed %s\n",
+                               strerror(errno)));
+                       return;
+               }
+       }
+}
+
+static bool cups_cache_reload_async(int fd)
 {
        TALLOC_CTX *frame = talloc_stackframe();
+       struct pcap_cache *tmp_pcap_cache = NULL;
        http_t          *http = NULL;           /* HTTP connection to server */
        ipp_t           *request = NULL,        /* IPP Request */
                        *response = NULL;       /* IPP Response */
@@ -226,7 +263,7 @@ bool cups_cache_reload(void)
                if (name == NULL)
                        break;
 
-               if (!pcap_cache_add(name, info)) {
+               if (!pcap_cache_add_specific(&tmp_pcap_cache, name, info)) {
                        goto out;
                }
        }
@@ -318,7 +355,7 @@ bool cups_cache_reload(void)
                if (name == NULL)
                        break;
 
-               if (!pcap_cache_add(name, info)) {
+               if (!pcap_cache_add_specific(&tmp_pcap_cache, name, info)) {
                        goto out;
                }
        }
@@ -335,10 +372,178 @@ bool cups_cache_reload(void)
        if (http)
                httpClose(http);
 
+       /* Send all the entries up the pipe. */
+       if (tmp_pcap_cache) {
+               pcap_printer_fn_specific(tmp_pcap_cache,
+                               send_pcap_info,
+                               (void *)&fd);
+
+               pcap_cache_destroy_specific(&tmp_pcap_cache);
+       }
        TALLOC_FREE(frame);
        return ret;
 }
 
+static struct pcap_cache *local_pcap_copy;
+struct fd_event *cache_fd_event;
+
+static bool cups_pcap_load_async(int *pfd)
+{
+       int fds[2];
+       pid_t pid;
+
+       if (cache_fd_event) {
+               DEBUG(3,("cups_pcap_load_async: already waiting for "
+                       "a refresh event\n" ));
+               return false;
+       }
+
+       DEBUG(5,("cups_pcap_load_async: asynchronously loading cups printers\n"));
+
+       if (pipe(fds) == -1) {
+               return false;
+       }
+
+       pid = sys_fork();
+       if (pid == (pid_t)-1) {
+               DEBUG(10,("cups_pcap_load_async: fork failed %s\n",
+                       strerror(errno) ));
+               close(fds[0]);
+               close(fds[1]);
+               return false;
+       }
+
+       if (pid) {
+               DEBUG(10,("cups_pcap_load_async: child pid = %u\n",
+                       (unsigned int)pid ));
+               /* Parent. */
+               close(fds[1]);
+               *pfd = fds[0];
+               return true;
+       }
+
+       /* Child. */
+       close(fds[0]);
+       cups_cache_reload_async(fds[1]);
+       close(fds[1]);
+       _exit(0);
+}
+
+static void cups_async_callback(struct event_context *event_ctx,
+                               struct fd_event *event,
+                               uint16 flags,
+                               void *p)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       int fd = *(int *)p;
+       struct pcap_cache *tmp_pcap_cache = NULL;
+
+       DEBUG(5,("cups_async_callback: callback received for printer data. "
+               "fd = %d\n", fd));
+
+       TALLOC_FREE(cache_fd_event);
+
+       while (1) {
+               char *name = NULL, *info = NULL;
+               size_t namelen = 0, infolen = 0;
+
+               if (sys_read(fd, &namelen, sizeof(namelen)) !=
+                               sizeof(namelen)) {
+                       DEBUG(10,("cups_async_callback: namelen read failed %d %s\n",
+                               errno, strerror(errno)));
+                       break;
+               }
+               if (sys_read(fd, &infolen, sizeof(infolen)) !=
+                               sizeof(infolen)) {
+                       DEBUG(10,("cups_async_callback: infolen read failed %s\n",
+                               strerror(errno)));
+                       break;
+               }
+               if (namelen) {
+                       name = TALLOC_ARRAY(frame, char, namelen);
+                       if (!name) {
+                               break;
+                       }
+                       if (sys_read(fd, name, namelen) != namelen) {
+                               DEBUG(10,("cups_async_callback: name read failed %s\n",
+                                       strerror(errno)));
+                               break;
+                       }
+               } else {
+                       name = NULL;
+               }
+               if (infolen) {
+                       info = TALLOC_ARRAY(frame, char, infolen);
+                       if (!info) {
+                               break;
+                       }
+                       if (sys_read(fd, info, infolen) != infolen) {
+                               DEBUG(10,("cups_async_callback: info read failed %s\n",
+                                       strerror(errno)));
+                               break;
+                       }
+               } else {
+                       info = NULL;
+               }
+
+               /* Add to our local pcap cache. */
+               pcap_cache_add_specific(&tmp_pcap_cache, name, info);
+               TALLOC_FREE(name);
+               TALLOC_FREE(info);
+       }
+
+       TALLOC_FREE(frame);
+       if (tmp_pcap_cache) {
+               /* We got a namelist, replace our local cache. */
+               pcap_cache_destroy_specific(&local_pcap_copy);
+               local_pcap_copy = tmp_pcap_cache;
+
+               /* And the systemwide pcap cache. */
+               pcap_cache_replace(local_pcap_copy);
+       } else {
+               DEBUG(2,("cups_async_callback: failed to read a new "
+                       "printer list\n"));
+       }
+       close(fd);
+}
+
+bool cups_cache_reload(void)
+{
+       int fd = -1;
+
+       /* Set up an async refresh. */
+       if (!cups_pcap_load_async(&fd)) {
+               return false;
+       }
+       if (!local_pcap_copy) {
+               /* We have no local cache, wait directly for
+                * async refresh to complete.
+                */
+               cups_async_callback(smbd_event_context(),
+                                       NULL,
+                                       EVENT_FD_READ,
+                                       (void *)&fd);
+               if (!local_pcap_copy) {
+                       return false;
+               }
+       } else {
+               /* Replace the system cache with our
+                * local copy. */
+               pcap_cache_replace(local_pcap_copy);
+
+               /* Trigger an event when the pipe can be read. */
+               cache_fd_event = event_add_fd(smbd_event_context(),
+                                       NULL, fd,
+                                       EVENT_FD_READ,
+                                       cups_async_callback,
+                                       (void *)&fd);
+               if (!cache_fd_event) {
+                       close(fd);
+                       return false;
+               }
+       }
+       return true;
+}
 
 /*
  * 'cups_job_delete()' - Delete a job.