#define _MAIN_C_
#include "cupsd.h"
#include <sys/resource.h>
#include <syslog.h>
#include <grp.h>
#if defined(HAVE_MALLOC_H) && defined(HAVE_MALLINFO)
# include <malloc.h>
#endif
#ifdef __APPLE__
#include <mach/mach.h>
#include <mach/mach_error.h>
#include <servers/bootstrap.h>
#ifdef HAVE_NOTIFY_H
#include <notify.h>
static time_t NotifyPostDelay = 0;
#endif
static kern_return_t registerBootstrapService();
static kern_return_t destroyBootstrapService();
static void emptyReceivePort();
static mach_port_t server_priv_port = MACH_PORT_NULL;
static mach_port_t service_rcv_port = MACH_PORT_NULL;
static char service_name[] = "/usr/sbin/cupsd";
#endif
static void parent_handler(int sig);
static void process_children(void);
static void sigchld_handler(int sig);
static void sighup_handler(int sig);
static void sigterm_handler(int sig);
static long select_timeout(int fds);
static void usage(void);
static int parent_signal = 0;
static int holdcount = 0;
#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
static sigset_t holdmask;
#endif
static int dead_children = 0;
static int stop_scheduler = 0;
int
main(int argc,
char *argv[])
{
int i;
char *opt;
int fg;
int fds;
client_t *con,
*next_con;
job_t *job,
*next;
listener_t *lis;
time_t activity;
time_t browse_time;
time_t senddoc_time;
#ifdef HAVE_DNSSD
DNSServiceErrorType sdErr;
printer_t *p;
dnssd_resolve_t *dnssdResolve,
*nextDNSSDResolve;
#endif
#ifdef HAVE_MALLINFO
time_t mallinfo_time;
#endif
struct timeval timeout;
struct rlimit limit;
#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
struct sigaction action;
#endif
#ifdef __sgi
cups_file_t *fp;
struct stat statbuf;
#endif
#ifdef __APPLE__
int debug = 0;
int lazy = 0;
#endif
fg = 0;
for (i = 1; i < argc; i ++)
if (argv[i][0] == '-')
for (opt = argv[i] + 1; *opt != '\0'; opt ++)
switch (*opt)
{
case 'c' :
i ++;
if (i >= argc)
usage();
if (argv[i][0] == '/')
{
SetString(&ConfigurationFile, argv[i]);
}
else
{
char *current;
if ((current = getcwd(NULL, 0)) == NULL)
exit(1);
SetStringf(&ConfigurationFile, "%s/%s", current, argv[i]);
free(current);
}
break;
case 'f' :
fg = 1;
break;
case 'F' :
fg = -1;
break;
#ifdef __APPLE__
case 'd' :
debug = 1;
break;
case 'L' :
lazy = 1;
break;
#endif
default :
fprintf(stderr, "cupsd: Unknown option \'%c\' - aborting!\n", *opt);
usage();
break;
}
else
{
fprintf(stderr, "cupsd: Unknown argument \'%s\' - aborting!\n", argv[i]);
usage();
}
if (!ConfigurationFile)
SetString(&ConfigurationFile, CUPS_SERVERROOT "/cupsd.conf");
if (!fg)
{
#ifdef HAVE_SIGSET
sigset(SIGUSR1, parent_handler);
sigset(SIGCHLD, parent_handler);
sigset(SIGHUP, SIG_IGN);
#elif defined(HAVE_SIGACTION)
memset(&action, 0, sizeof(action));
sigemptyset(&action.sa_mask);
sigaddset(&action.sa_mask, SIGUSR1);
action.sa_handler = parent_handler;
sigaction(SIGUSR1, &action, NULL);
sigaction(SIGCHLD, &action, NULL);
sigemptyset(&action.sa_mask);
action.sa_handler = SIG_IGN;
sigaction(SIGHUP, &action, NULL);
#else
signal(SIGUSR1, parent_handler);
signal(SIGCLD, parent_handler);
signal(SIGHUP, SIG_IGN);
#endif
if (fork() > 0)
{
for (; parent_signal == 0;)
sleep(1);
if (parent_signal == SIGUSR1)
return (0);
if (wait(&i) < 0)
{
perror("cupsd");
return (1);
}
else if (WIFEXITED(i))
{
fprintf(stderr, "cupsd: Child exited with status %d!\n", WEXITSTATUS(i));
return (2);
}
else
{
fprintf(stderr, "cupsd: Child exited on signal %d!\n", WTERMSIG(i));
return (3);
}
}
}
if (fg < 1)
{
chdir("/");
#ifndef DEBUG
getrlimit(RLIMIT_CORE, &limit);
limit.rlim_cur = 0;
setrlimit(RLIMIT_CORE, &limit);
close(0);
close(1);
close(2);
setsid();
#endif
}
if (getenv("TZ") != NULL)
SetStringf(&TZ, "TZ=%s", getenv("TZ"));
else
SetString(&TZ, "");
tzset();
#ifdef LC_TIME
setlocale(LC_TIME, "");
#endif
getrlimit(RLIMIT_NOFILE, &limit);
if (limit.rlim_max > CUPS_MAX_FDS)
MaxFDs = CUPS_MAX_FDS;
else
MaxFDs = limit.rlim_max;
limit.rlim_cur = MaxFDs;
setrlimit(RLIMIT_NOFILE, &limit);
SetSize = (MaxFDs + 31) / 8 + 4;
if (SetSize < sizeof(fd_set))
SetSize = sizeof(fd_set);
InputSet = (fd_set *)calloc(1, SetSize);
OutputSet = (fd_set *)calloc(1, SetSize);
InputFds = (fd_set *)calloc(1, SetSize);
OutputFds = (fd_set *)calloc(1, SetSize);
if (InputSet == NULL || OutputSet == NULL || InputFds == NULL || OutputFds == NULL)
{
syslog(LOG_LPR, "Unable to allocate memory for select() sets - exiting!");
return (1);
}
if (!ReadConfiguration())
{
syslog(LOG_LPR, "Unable to read configuration file \'%s\' - exiting!",
ConfigurationFile);
return (1);
}
#ifdef HAVE_SIGSET
if (RunAsUser)
sigset(SIGHUP, sigterm_handler);
else
sigset(SIGHUP, sighup_handler);
sigset(SIGPIPE, SIG_IGN);
sigset(SIGTERM, sigterm_handler);
#elif defined(HAVE_SIGACTION)
memset(&action, 0, sizeof(action));
sigemptyset(&action.sa_mask);
sigaddset(&action.sa_mask, SIGHUP);
if (RunAsUser)
action.sa_handler = sigterm_handler;
else
action.sa_handler = sighup_handler;
sigaction(SIGHUP, &action, NULL);
sigemptyset(&action.sa_mask);
action.sa_handler = SIG_IGN;
sigaction(SIGPIPE, &action, NULL);
sigemptyset(&action.sa_mask);
sigaddset(&action.sa_mask, SIGTERM);
sigaddset(&action.sa_mask, SIGCHLD);
action.sa_handler = sigterm_handler;
sigaction(SIGTERM, &action, NULL);
#else
if (RunAsUser)
signal(SIGHUP, sigterm_handler);
else
signal(SIGHUP, sighup_handler);
signal(SIGPIPE, SIG_IGN);
signal(SIGTERM, sigterm_handler);
#endif
#ifdef __sgi
if ((fp = cupsFileOpen("/var/spool/lp/SCHEDLOCK", "w")) == NULL)
{
syslog(LOG_LPR, "Unable to create fake lpsched lock file "
"\"/var/spool/lp/SCHEDLOCK\"\' - %s!",
strerror(errno));
}
else
{
fchmod(cupsFileNumber(fp), 0644);
fchown(cupsFileNumber(fp), User, Group);
cupsFileClose(fp);
}
#endif
InitCerts();
if (!fg)
kill(getppid(), SIGUSR1);
#ifdef __APPLE__
if (!debug)
registerBootstrapService();
StartSysEventMonitor();
#endif
if (RunAsUser)
{
setgid(Group);
setgroups(1, &Group);
setuid(User);
}
CheckJobs();
#ifdef __APPLE__
if (lazy && NumBrowsers == 0 && NumJobs == 0)
{
LogMessage(L_INFO, "Printer sharing is off and there are no jobs pending, will restart on demand. Exiting.");
return (0);
}
#endif
#ifdef HAVE_NOTIFY_POST
NotifyPost = (CUPS_NOTIFY_PRINTER_LIST | CUPS_NOTIFY_JOB);
#endif
#ifdef HAVE_MALLINFO
mallinfo_time = 0;
#endif
browse_time = time(NULL);
senddoc_time = time(NULL);
fds = 1;
while (!stop_scheduler)
{
#ifdef DEBUG
LogMessage(L_DEBUG2, "main: Top of loop, dead_children=%d, NeedReload=%d",
dead_children, NeedReload);
#endif
#ifdef __APPLE__
emptyReceivePort();
#endif __APPLE__
if (dead_children)
process_children();
if (NeedReload)
{
if (NumClients > 0)
{
for (con = Clients; con != NULL; con = next_con)
{
next_con = con->next;
if (con->http.state == HTTP_WAITING)
CloseClient(con);
else
con->http.keep_alive = HTTP_KEEPALIVE_OFF;
}
PauseListening();
}
for (job = Jobs; job; job = job->next)
if (job->state->values[0].integer == IPP_JOB_PROCESSING)
break;
if ((NumClients == 0 && (!job || NeedReload != RELOAD_ALL)) ||
(time(NULL) - ReloadTime) >= ReloadTimeout)
{
if (!ReadConfiguration())
{
syslog(LOG_LPR, "Unable to read configuration file \'%s\' - exiting!",
ConfigurationFile);
break;
}
}
}
memcpy(InputFds, InputSet, SetSize);
memcpy(OutputFds, OutputSet, SetSize);
timeout.tv_sec = select_timeout(fds);
timeout.tv_usec = 0;
if ((fds = select(MaxFDs, InputFds, OutputFds, NULL, &timeout)) < 0)
{
char s[16384],
*sptr;
int slen;
if (errno == EINTR)
continue;
LogMessage(L_EMERG, "select() failed - %s!", strerror(errno));
strcpy(s, "InputSet =");
slen = 10;
sptr = s + 10;
for (i = 0; i < MaxFDs; i ++)
if (FD_ISSET(i, InputSet))
{
snprintf(sptr, sizeof(s) - slen, " %d", i);
slen += strlen(sptr);
sptr += strlen(sptr);
}
LogMessage(L_EMERG, s);
strcpy(s, "OutputSet =");
slen = 11;
sptr = s + 11;
for (i = 0; i < MaxFDs; i ++)
if (FD_ISSET(i, OutputSet))
{
snprintf(sptr, sizeof(s) - slen, " %d", i);
slen += strlen(sptr);
sptr += strlen(sptr);
}
LogMessage(L_EMERG, s);
for (con = Clients; con != NULL; con = con->next)
LogMessage(L_EMERG, "Clients[%p] = %d, file = %d, state = %d",
con, con->http.fd, con->file, con->http.state);
for (i = 0, lis = Listeners; i < NumListeners; i ++, lis ++)
LogMessage(L_EMERG, "Listeners[%d] = %d", i, lis->fd);
LogMessage(L_EMERG, "BrowseSocket = %d", BrowseSocket);
LogMessage(L_EMERG, "CGIPipes[0] = %d", CGIPipes[0]);
for (job = Jobs; job != NULL; job = job->next)
LogMessage(L_EMERG, "Jobs[%d] = %d", job->id, job->pipe);
LogMessage(L_EMERG, "SysEventPipes[0] = %d", SysEventPipes[0]);
#ifdef HAVE_DNSSD
LogMessage(L_EMERG, "BrowseDNSSDfd = %d", BrowseDNSSDfd);
for (dnssdResolve = DNSSDPendingResolves; dnssdResolve; dnssdResolve = dnssdResolve->next)
LogMessage(L_EMERG, "dnssdResolve fd = %d", dnssdResolve->fd);
for (p = Printers; p != NULL; p = p->next)
LogMessage(L_EMERG, "printer[%s] %d, %d", p->name, p->dnssd_ipp_fd, p->dnssd_query_fd);
#endif
break;
}
for (i = NumListeners, lis = Listeners; i > 0; i --, lis ++)
if (FD_ISSET(lis->fd, InputFds))
AcceptClient(lis);
for (con = Clients; con != NULL; con = next_con)
{
next_con = con->next;
if (FD_ISSET(con->http.fd, InputFds) || con->http.used)
if (!ReadClient(con))
continue;
if (con->pipe_pid && FD_ISSET(con->file, InputFds))
{
con->file_ready = 1;
#ifdef DEBUG
LogMessage(L_DEBUG2, "main: Data ready file %d!", con->file);
#endif
if (!FD_ISSET(con->http.fd, OutputFds))
{
LogMessage(L_DEBUG2, "main: Removing fd %d from InputSet...", con->file);
FD_CLR(con->file, InputFds);
FD_CLR(con->file, InputSet);
}
}
if (FD_ISSET(con->http.fd, OutputFds) &&
(!con->pipe_pid || con->file_ready))
if (!WriteClient(con))
continue;
activity = time(NULL) - Timeout;
if (con->http.activity < activity && !con->pipe_pid)
{
LogMessage(L_DEBUG, "Closing client %d after %d seconds of inactivity...",
con->http.fd, Timeout);
CloseClient(con);
continue;
}
}
for (job = Jobs; job != NULL; job = next)
{
next = job->next;
if (job->pipe >= 0 && FD_ISSET(job->pipe, InputFds))
{
UpdateJob(job);
}
}
if (CGIPipes[0] >= 0 && FD_ISSET(CGIPipes[0], InputFds))
UpdateCGI();
#ifdef __APPLE__
if (SysEventPipes[0] >= 0 && FD_ISSET(SysEventPipes[0], InputFds))
UpdateSysEventMonitor();
#endif
if (Browsing && BrowseRemoteProtocols)
{
if (BrowseSocket >= 0 && FD_ISSET(BrowseSocket, InputFds))
UpdateCUPSBrowse();
if (PollPipe >= 0 && FD_ISSET(PollPipe, InputFds))
UpdatePolling();
#ifdef HAVE_LIBSLP
if ((BrowseRemoteProtocols & BROWSE_SLP) && BrowseSLPRefresh <= time(NULL))
UpdateSLPBrowse();
#endif
#ifdef HAVE_DNSSD
if (BrowseDNSSDRef && FD_ISSET(BrowseDNSSDfd, InputFds))
{
if ((sdErr = DNSServiceProcessResult(BrowseDNSSDRef)) != kDNSServiceErr_NoError)
{
LogMessage(L_ERROR, "DNS Service Discovery browsing error %d; removing fd %d from InputSet...",
sdErr, BrowseDNSSDfd);
FD_CLR(BrowseDNSSDfd, InputFds);
FD_CLR(BrowseDNSSDfd, InputSet);
DNSServiceRefDeallocate(BrowseDNSSDRef);
BrowseDNSSDRef = NULL;
BrowseDNSSDfd = -1;
}
}
for (dnssdResolve = DNSSDPendingResolves; dnssdResolve;)
{
nextDNSSDResolve = dnssdResolve->next;
if (dnssdResolve->sdRef && FD_ISSET(dnssdResolve->fd, InputFds))
{
if ((sdErr = DNSServiceProcessResult(dnssdResolve->sdRef)) != kDNSServiceErr_NoError)
{
LogMessage(L_ERROR, "DNS Service Discovery resolving error %d; removing fd %d from InputSet...",
sdErr, dnssdResolve->fd);
FD_CLR(dnssdResolve->fd, InputFds);
FD_CLR(dnssdResolve->fd, InputSet);
DNSServiceRefDeallocate(dnssdResolve->sdRef);
dnssdResolve->sdRef = NULL;
dnssdResolve->fd = -1;
}
}
dnssdResolve = nextDNSSDResolve;
}
for (p = Printers; p != NULL; p = p->next)
{
if (p->dnssd_ipp_ref && FD_ISSET(p->dnssd_ipp_fd, InputFds))
{
if ((sdErr = DNSServiceProcessResult(p->dnssd_ipp_ref)) != kDNSServiceErr_NoError)
{
LogMessage(L_ERROR, "DNS Service Discovery IPP registration error %d; removing fd %d from InputSet...",
sdErr, p->dnssd_ipp_fd);
FD_CLR(p->dnssd_ipp_fd, InputFds);
FD_CLR(p->dnssd_ipp_fd, InputSet);
DNSServiceRefDeallocate(p->dnssd_ipp_ref);
p->dnssd_ipp_ref = NULL;
p->dnssd_ipp_fd = -1;
}
}
if (p->dnssd_query_ref && FD_ISSET(p->dnssd_query_fd, InputFds))
{
if ((sdErr = DNSServiceProcessResult(p->dnssd_query_ref)) != kDNSServiceErr_NoError)
{
LogMessage(L_ERROR, "DNS Service Discovery query error %d; removing fd %d from InputSet...",
sdErr, p->dnssd_query_fd);
FD_CLR(p->dnssd_query_fd, InputFds);
FD_CLR(p->dnssd_query_fd, InputSet);
DNSServiceRefDeallocate(p->dnssd_query_ref);
p->dnssd_query_ref = NULL;
p->dnssd_query_fd = -1;
}
}
}
#endif
if (time(NULL) > browse_time)
{
SendBrowseList();
browse_time = time(NULL);
}
}
if ((time(NULL) - senddoc_time) >= 10)
{
CheckJobs();
senddoc_time = time(NULL);
}
#ifdef HAVE_MALLINFO
if ((time(NULL) - mallinfo_time) >= 60 && LogLevel >= L_DEBUG)
{
struct mallinfo mem;
mem = mallinfo();
LogMessage(L_DEBUG, "mallinfo: arena = %d, used = %d, free = %d\n",
mem.arena, mem.usmblks + mem.uordblks,
mem.fsmblks + mem.fordblks);
mallinfo_time = time(NULL);
}
#endif
if ((time(NULL) - RootCertTime) >= RootCertDuration && RootCertDuration)
{
DeleteCert(0);
AddCert(0, "root");
}
#ifdef HAVE_NOTIFY_POST
if (NotifyPost && NotifyPostDelay <= time(NULL))
{
#ifdef DEBUG
static time_t first_notify_post = 0;
if (!first_notify_post) first_notify_post = time(NULL);
fprintf(stderr, "%d notify_post()\n", (int)(time(NULL) - first_notify_post));
#endif
if ((NotifyPost & CUPS_NOTIFY_PRINTER_LIST))
notify_post("com.apple.printerListChange");
if ((NotifyPost & CUPS_NOTIFY_PRINTER_HISTORY))
notify_post("com.apple.printerHistoryChange");
if ((NotifyPost & CUPS_NOTIFY_JOB))
notify_post("com.apple.jobChange");
NotifyPost = 0;
NotifyPostDelay = time(NULL) + 1;
}
#endif
}
if (stop_scheduler)
LogMessage(L_INFO, "Scheduler shutting down normally.");
else
LogMessage(L_ERROR, "Scheduler shutting down due to program error.");
StopServer();
StopAllJobs();
#ifdef __APPLE__
StopSysEventMonitor();
#endif
#ifdef HAVE_NOTIFY_POST
fprintf(stderr, "notify_post(com.apple.printerListChange) last\n");
notify_post("com.apple.printerListChange");
#endif
#ifdef __sgi
if (!stat("/var/spool/lp/FIFO", &statbuf))
if (!S_ISFIFO(statbuf.st_mode))
unlink("/var/spool/lp/SCHEDLOCK");
#endif
#ifdef __APPLE__
destroyBootstrapService();
#endif
free(InputSet);
free(OutputSet);
free(InputFds);
free(OutputFds);
return (!stop_scheduler);
}
void
cupsdClosePipe(int *fds)
{
if (fds[0] >= 0)
{
close(fds[0]);
fds[0] = -1;
}
if (fds[1] >= 0)
{
close(fds[1]);
fds[1] = -1;
}
}
int
cupsdOpenPipe(int *fds)
{
if (pipe(fds))
return (-1);
if (fcntl(fds[0], F_SETFD, fcntl(fds[0], F_GETFD) | FD_CLOEXEC))
{
close(fds[0]);
close(fds[1]);
return (-1);
}
if (fcntl(fds[1], F_SETFD, fcntl(fds[1], F_GETFD) | FD_CLOEXEC))
{
close(fds[0]);
close(fds[1]);
return (-1);
}
return (0);
}
void
CatchChildSignals(void)
{
#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
struct sigaction action;
#endif
#ifdef HAVE_SIGSET
sigset(SIGCHLD, sigchld_handler);
#elif defined(HAVE_SIGACTION)
memset(&action, 0, sizeof(action));
sigemptyset(&action.sa_mask);
sigaddset(&action.sa_mask, SIGTERM);
sigaddset(&action.sa_mask, SIGCHLD);
action.sa_handler = sigchld_handler;
sigaction(SIGCHLD, &action, NULL);
#else
signal(SIGCLD, sigchld_handler);
#endif
}
void
ClearString(char **s)
{
if (s && *s)
{
free(*s);
*s = NULL;
}
}
void
HoldSignals(void)
{
#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
sigset_t newmask;
#endif
holdcount ++;
if (holdcount > 1)
return;
#ifdef HAVE_SIGSET
sighold(SIGTERM);
sighold(SIGCHLD);
#elif defined(HAVE_SIGACTION)
sigemptyset(&newmask);
sigaddset(&newmask, SIGTERM);
sigaddset(&newmask, SIGCHLD);
sigprocmask(SIG_BLOCK, &newmask, &holdmask);
#endif
}
void
IgnoreChildSignals(void)
{
#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
struct sigaction action;
#endif
#ifdef HAVE_SIGSET
sigset(SIGCHLD, SIG_DFL);
#elif defined(HAVE_SIGACTION)
memset(&action, 0, sizeof(action));
sigemptyset(&action.sa_mask);
sigaddset(&action.sa_mask, SIGCHLD);
action.sa_handler = SIG_DFL;
sigaction(SIGCHLD, &action, NULL);
#else
signal(SIGCLD, SIG_DFL);
#endif
}
void
ReleaseSignals(void)
{
holdcount --;
if (holdcount > 0)
return;
#ifdef HAVE_SIGSET
sigrelse(SIGTERM);
sigrelse(SIGCHLD);
#elif defined(HAVE_SIGACTION)
sigprocmask(SIG_SETMASK, &holdmask, NULL);
#endif
}
void
SetString(char **s,
const char *v)
{
if (!s || *s == v)
return;
if (*s)
free(*s);
if (v)
*s = strdup(v);
else
*s = NULL;
}
void
SetStringf(char **s,
const char *f,
...)
{
char v[1024];
va_list ap;
char *olds;
if (!s)
return;
olds = *s;
if (f)
{
va_start(ap, f);
vsnprintf(v, sizeof(v), f, ap);
va_end(ap);
*s = strdup(v);
}
else
*s = NULL;
if (olds)
free(olds);
}
static void
parent_handler(int sig)
{
parent_signal = sig;
}
static void
process_children(void)
{
int status;
int pid;
job_t *job;
int i;
char *filter;
dead_children = 0;
#ifdef HAVE_WAITPID
while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
#elif defined(HAVE_WAIT3)
while ((pid = wait3(&status, WNOHANG, NULL)) > 0)
#else
if ((pid = wait(&status)) > 0)
#endif
{
DEBUG_printf(("process_children: pid = %d, status = %d\n", pid, status));
if (status == SIGTERM)
status = 0;
if (status)
{
if (WIFSIGNALED(status))
LogMessage(L_ERROR, "PID %d crashed on signal %d!", pid,
WTERMSIG(status));
else
LogMessage(L_ERROR, "PID %d stopped with status %d!", pid,
WEXITSTATUS(status));
if (LogLevel < L_DEBUG)
LogMessage(L_INFO, "Hint: Try setting the LogLevel to \"debug\" to find out more.");
}
else
LogMessage(L_DEBUG, "PID %d exited with no errors.", pid);
if (pid)
DeleteCert(pid);
for (job = Jobs; job != NULL; job = job->next)
if (job->state != NULL &&
job->state->values[0].integer == IPP_JOB_PROCESSING)
{
for (i = 0; job->procs[i]; i ++)
if (job->procs[i] == pid)
break;
if (job->procs[i])
{
job->procs[i] = -pid;
if (status && job->status >= 0)
{
if (!job->procs[i + 1])
job->status = -status;
else
job->status = status;
if ( !(job->printer->type & CUPS_PRINTER_FAX))
{
if ((filter = basename(job->filters[i])) == NULL)
filter = "";
if (WIFSIGNALED(status))
snprintf(job->printer->state_message, sizeof(job->printer->state_message),
"The process \"%s\" terminated unexpectedly on signal %d",
filter, WTERMSIG(status));
else
snprintf(job->printer->state_message, sizeof(job->printer->state_message),
"The process \"%s\" stopped unexpectedly with status %d",
filter, WEXITSTATUS(status));
AddPrinterHistory(job->printer);
}
}
break;
}
}
}
}
static void
sigchld_handler(int sig)
{
(void)sig;
dead_children = 1;
#if !defined(HAVE_SIGSET) && !defined(HAVE_SIGACTION)
signal(SIGCLD, sigchld_handler);
#endif
}
static void
sighup_handler(int sig)
{
(void)sig;
NeedReload = RELOAD_ALL;
ReloadTime = time(NULL);
#if !defined(HAVE_SIGSET) && !defined(HAVE_SIGACTION)
signal(SIGHUP, sighup_handler);
#endif
}
static void
sigterm_handler(int sig)
{
(void)sig;
stop_scheduler = 1;
}
static long
select_timeout(int fds)
{
long timeout;
time_t now;
client_t *con;
printer_t *p;
job_t *job;
const char *why;
for (con = Clients; con != NULL; con = con->next)
if (con->http.used > 0)
return (0);
if (fds || NumClients > 50)
return (1);
#ifdef HAVE_NOTIFY_POST
if (NotifyPost)
return (1);
#endif
now = time(NULL);
timeout = now + 86400;
why = "do nothing";
for (con = Clients; con != NULL; con = con->next)
if ((con->http.activity + Timeout) < timeout)
{
timeout = con->http.activity + Timeout;
why = "timeout a client connection";
}
if (Browsing && (BrowseLocalProtocols || BrowseRemoteProtocols))
{
#ifdef HAVE_LIBSLP
if ((BrowseRemoteProtocols & BROWSE_SLP) && (BrowseSLPRefresh < timeout))
{
timeout = BrowseSLPRefresh;
why = "update SLP browsing";
}
#endif
if ((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_CUPS)
{
for (p = Printers; p != NULL; p = p->next)
{
if (p->type & CUPS_PRINTER_REMOTE)
{
if (p->browse_protocol == BROWSE_CUPS && (p->browse_time + BrowseTimeout) < timeout)
{
timeout = p->browse_time + BrowseTimeout;
why = "browse timeout a printer";
}
}
else if (!(p->type & CUPS_PRINTER_IMPLICIT))
{
if (BrowseInterval && (p->browse_time + BrowseInterval) < timeout)
{
timeout = p->browse_time + BrowseInterval;
why = "send browse update";
}
}
}
}
}
if (timeout > (now + 10))
{
for (job = Jobs; job != NULL; job = job->next)
if (job->state->values[0].integer <= IPP_JOB_PROCESSING)
{
timeout = now + 10;
why = "process active jobs";
break;
}
}
#ifdef HAVE_MALLINFO
if (LogLevel >= L_DEBUG && (mallinfo_time + 60) < timeout)
{
timeout = mallinfo_time + 60;
why = "display memory usage";
}
#endif
if (RootCertDuration && (RootCertTime + RootCertDuration) < timeout)
{
timeout = RootCertTime + RootCertDuration;
why = "update root certificate";
}
timeout = timeout - now + 1;
if (timeout < 1)
timeout = 1;
else if (timeout > 86400)
timeout = 86400;
LogMessage(L_DEBUG2, "select_timeout: %ld seconds to %s", timeout, why);
return (timeout);
}
static void
usage(void)
{
#ifdef __APPLE__
static const char usage_msg[] = \
"Usage: cupsd [-c config-file] [-f] [-F] [-d] [-L]\n" \
" -c Use specified configuration file.\n" \
" -d Debugging mode, don't auto-relaunch on process termination.\n" \
" -f Run in foreground.\n" \
" -F Run in foreground but still disconnect from terminal.\n" \
" -L Lazy mode.\n";
fputs(usage_msg, stderr);
#else
fputs("Usage: cupsd [-c config-file] [-f] [-F]\n", stderr);
#endif
exit(1);
}
#ifdef __APPLE__
static kern_return_t registerBootstrapService()
{
kern_return_t status;
mach_port_t service_send_port;
status = bootstrap_check_in(bootstrap_port, (char*)service_name, &service_rcv_port);
if (status == KERN_SUCCESS)
{
server_priv_port = bootstrap_port;
}
else if (status == BOOTSTRAP_UNKNOWN_SERVICE)
{
status = bootstrap_create_server(bootstrap_port, "/usr/sbin/cupsd -f", getuid(),
true , &server_priv_port);
if (status != KERN_SUCCESS)
return status;
status = bootstrap_create_service(server_priv_port, (char*)service_name, &service_send_port);
if (status != KERN_SUCCESS)
{
mach_port_deallocate(mach_task_self(), server_priv_port);
return status;
}
status = bootstrap_check_in(server_priv_port, (char*)service_name, &service_rcv_port);
if (status != KERN_SUCCESS)
{
mach_port_deallocate(mach_task_self(), server_priv_port);
mach_port_deallocate(mach_task_self(), service_send_port);
return status;
}
}
return status;
}
static void emptyReceivePort()
{
mach_msg_empty_rcv_t aMsg;
kern_return_t msg_rcv_result,
msg_snd_result;
if (service_rcv_port != MACH_PORT_NULL)
{
do
{
aMsg.header.msgh_size = sizeof(aMsg);
msg_rcv_result = mach_msg(&aMsg.header, MACH_RCV_MSG | MACH_RCV_TIMEOUT, 0,
aMsg.header.msgh_size, service_rcv_port, 0, MACH_PORT_NULL);
if (msg_rcv_result == MACH_MSG_SUCCESS)
{
aMsg.header.msgh_bits = MACH_MSGH_BITS( MACH_MSG_TYPE_MOVE_SEND, MACH_MSG_TYPE_MAKE_SEND );
aMsg.header.msgh_size = sizeof(aMsg.header);
msg_snd_result = mach_msg(&aMsg.header, MACH_SEND_MSG | MACH_SEND_TIMEOUT, aMsg.header.msgh_size,
0, MACH_PORT_NULL, 500, MACH_PORT_NULL);
if (msg_snd_result != KERN_SUCCESS)
LogMessage(L_WARN, "emptyReceivePort: mach_msg send returns: %s", mach_error_string(msg_snd_result));
if (msg_snd_result == MACH_SEND_INVALID_DEST || msg_snd_result == MACH_SEND_TIMED_OUT)
mach_msg_destroy(&aMsg.header);
}
else if (msg_rcv_result != MACH_RCV_TIMED_OUT)
LogMessage(L_WARN, "emptyReceivePort: mach_msg receive returns: %s", mach_error_string(msg_rcv_result));
}
while(msg_rcv_result == MACH_MSG_SUCCESS);
}
}
static kern_return_t destroyBootstrapService()
{
if (service_rcv_port != MACH_PORT_NULL)
{
mach_port_destroy(mach_task_self(), service_rcv_port);
service_rcv_port = MACH_PORT_NULL;
}
return bootstrap_register(server_priv_port, (char*)service_name, MACH_PORT_NULL);
}
#endif