1
0
mirror of https://github.com/samba-team/samba.git synced 2025-02-25 17:57:42 +03:00

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.
This commit is contained in:
Jeremy Allison 2008-10-10 11:55:14 -07:00
parent 5b75aa3406
commit eada8f8abe
6 changed files with 268 additions and 35 deletions

View 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 */

View 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;

View 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;

View 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);
}

View File

@ -63,41 +63,51 @@
#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 : ""));
return True;
p->next = *ppcache;
*ppcache = p;
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 *))
{
pcap_cache_t *p;
for (p = pcap_cache; p != NULL; p = p->next)
fn(p->name, p->comment);
void pcap_printer_fn_specific(const struct pcap_cache *pc,
void (*fn)(const char *, const char *, void *),
void *pdata)
{
const struct pcap_cache *p;
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);
}

View 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.