#include <cups/cups-private.h>
#include <signal.h>
static int restart_polling = 1;
static char *dequote(char *d, const char *s, int dlen);
static int poll_server(http_t *http, int sock, int port, int interval,
const char *prefix);
static void sighup_handler(int sig);
int
main(int argc,
char *argv[])
{
http_t *http;
int interval;
int sock;
int port;
int val;
int seconds,
remain;
char prefix[1024];
#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
struct sigaction action;
#endif
#ifdef HAVE_SIGSET
sigset(SIGHUP, sighup_handler);
#elif defined(HAVE_SIGACTION)
memset(&action, 0, sizeof(action));
sigemptyset(&action.sa_mask);
sigaddset(&action.sa_mask, SIGHUP);
action.sa_handler = sighup_handler;
sigaction(SIGHUP, &action, NULL);
#else
signal(SIGHUP, sighup_handler);
#endif
setbuf(stderr, NULL);
if (argc != 5)
{
fputs("Usage: cups-polld server server-port interval port\n", stderr);
return (1);
}
interval = atoi(argv[3]);
port = atoi(argv[4]);
if (interval < 2)
interval = 2;
snprintf(prefix, sizeof(prefix), "[cups-polld %s:%d]", argv[1], atoi(argv[2]));
if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
fprintf(stderr, "ERROR: %s Unable to open broadcast socket: %s\n", prefix,
strerror(errno));
return (1);
}
val = 1;
if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &val, sizeof(val)))
{
fprintf(stderr, "ERROR: %s Unable to put socket in broadcast mode: %s\n",
prefix, strerror(errno));
close(sock);
return (1);
}
for (http = NULL; !ferror(stderr);)
{
if (restart_polling || !http)
{
restart_polling = 0;
httpClose(http);
if ((http = httpConnectEncrypt(argv[1], atoi(argv[2]),
cupsEncryption())) == NULL)
{
fprintf(stderr, "ERROR: %s Unable to connect to %s on port %s.\n",
prefix, argv[1], argv[2]);
}
}
remain = interval;
if (http && (seconds = poll_server(http, sock, port, interval, prefix)) > 0)
remain -= seconds;
if (remain > 0 && !restart_polling)
sleep(remain);
}
return (1);
}
static char *
dequote(char *d,
const char *s,
int dlen)
{
char *dptr;
if (s)
{
for (dptr = d, dlen --; *s && dlen > 0; s ++)
if (*s != '\"')
{
*dptr++ = *s;
dlen --;
}
*dptr = '\0';
}
else
*d = '\0';
return (d);
}
static int
poll_server(http_t *http,
int sock,
int port,
int interval,
const char *prefix)
{
int seconds;
int count,
max_count;
ipp_t *request,
*response;
ipp_attribute_t *attr;
const char *uri;
char info[1024],
job_sheets[1024],
location[1024],
make_model[1024];
cups_ptype_t type;
ipp_pstate_t state;
int accepting;
struct sockaddr_in addr;
char packet[1540];
static const char * const attrs[] =
{
"job-sheets-default",
"printer-info",
"printer-is-accepting-jobs",
"printer-location",
"printer-make-and-model",
"printer-name",
"printer-state",
"printer-type",
"printer-uri-supported"
};
memset(&addr, 0, sizeof(addr));
addr.sin_addr.s_addr = htonl(0x7f000001);
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
request = ippNewRequest(CUPS_GET_PRINTERS);
ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
"requested-attributes", sizeof(attrs) / sizeof(attrs[0]),
NULL, attrs);
ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM,
"printer-type", 0);
ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM,
"printer-type-mask",
CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT |
CUPS_PRINTER_NOT_SHARED);
seconds = time(NULL);
response = cupsDoRequest(http, request, "/");
if (cupsLastError() > IPP_OK_CONFLICT)
{
fprintf(stderr, "ERROR: %s CUPS-Get-Printers failed: %s\n", prefix,
cupsLastErrorString());
ippDelete(response);
return (-1);
}
if (response)
{
for (attr = ippFindAttribute(response, "printer-name", IPP_TAG_NAME),
max_count = 0;
attr != NULL;
attr = ippFindNextAttribute(response, "printer-name", IPP_TAG_NAME),
max_count ++);
fprintf(stderr, "DEBUG: %s Found %d printers.\n", prefix, max_count);
count = 0;
max_count = 2 * max_count / interval + 1;
for (attr = response->attrs; attr; attr = attr->next)
{
while (attr && attr->group_tag != IPP_TAG_PRINTER)
attr = attr->next;
if (!attr)
break;
uri = NULL;
info[0] = '\0';
job_sheets[0] = '\0';
location[0] = '\0';
make_model[0] = '\0';
type = CUPS_PRINTER_REMOTE;
accepting = 1;
state = IPP_PRINTER_IDLE;
while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER)
{
if (!strcmp(attr->name, "job-sheets-default") &&
(attr->value_tag == IPP_TAG_NAME ||
attr->value_tag == IPP_TAG_KEYWORD))
{
if (attr->num_values == 1)
snprintf(job_sheets, sizeof(job_sheets), " job-sheets=%s",
attr->values[0].string.text);
else
snprintf(job_sheets, sizeof(job_sheets), " job-sheets=%s,%s",
attr->values[0].string.text,
attr->values[1].string.text);
}
else if (!strcmp(attr->name, "printer-uri-supported") &&
attr->value_tag == IPP_TAG_URI)
uri = attr->values[0].string.text;
else if (!strcmp(attr->name, "printer-info") &&
attr->value_tag == IPP_TAG_TEXT)
dequote(info, attr->values[0].string.text, sizeof(info));
else if (!strcmp(attr->name, "printer-is-accepting-jobs") &&
attr->value_tag == IPP_TAG_BOOLEAN)
accepting = attr->values[0].boolean;
else if (!strcmp(attr->name, "printer-location") &&
attr->value_tag == IPP_TAG_TEXT)
dequote(location, attr->values[0].string.text, sizeof(location));
else if (!strcmp(attr->name, "printer-make-and-model") &&
attr->value_tag == IPP_TAG_TEXT)
dequote(make_model, attr->values[0].string.text, sizeof(location));
else if (!strcmp(attr->name, "printer-state") &&
attr->value_tag == IPP_TAG_ENUM)
state = (ipp_pstate_t)attr->values[0].integer;
else if (!strcmp(attr->name, "printer-type") &&
attr->value_tag == IPP_TAG_ENUM)
type = (cups_ptype_t)attr->values[0].integer;
attr = attr->next;
}
if (uri == NULL)
{
if (attr == NULL)
break;
else
continue;
}
type |= CUPS_PRINTER_REMOTE;
if (!accepting)
type |= CUPS_PRINTER_REJECTING;
snprintf(packet, sizeof(packet),
"%x %x %s \"%s\" \"%s\" \"%s\" lease-duration=%d%s\n",
type, state, uri, location, info, make_model, interval * 2,
job_sheets);
fprintf(stderr, "DEBUG2: %s Sending %s", prefix, packet);
if (sendto(sock, packet, strlen(packet), 0,
(struct sockaddr *)&addr, sizeof(addr)) <= 0)
{
ippDelete(response);
perror("cups-polld");
return (-1);
}
count ++;
if (count >= max_count)
{
count = 0;
sleep(1);
}
if (!attr || restart_polling)
break;
}
ippDelete(response);
}
return (time(NULL) - seconds);
}
static void
sighup_handler(int sig)
{
(void)sig;
restart_polling = 1;
#if !defined(HAVE_SIGSET) && !defined(HAVE_SIGACTION)
signal(SIGHUP, sighup_handler);
#endif
}