From eada8f8abe6e4b770b7a2e279fc897a4272b6fa5 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 10 Oct 2008 11:55:14 -0700 Subject: If you have a large number of cups printers, then scanning for print info can cause a client to timeout (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/printing/print_cups.c | 211 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 208 insertions(+), 3 deletions(-) (limited to 'source3/printing/print_cups.c') diff --git a/source3/printing/print_cups.c b/source3/printing/print_cups.c index b9bed7a138..6fe24e181e 100644 --- a/source3/printing/print_cups.c +++ b/source3/printing/print_cups.c @@ -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. -- cgit