#define _MAIN_C_
#include "cupsd.h"
#include <sys/resource.h>
#include <syslog.h>
#if defined(HAVE_MALLOC_H) && defined(HAVE_MALLINFO)
# include <malloc.h>
#endif
#ifndef FD_SETSIZE
# define FD_SETSIZE 1024
#endif
static void sigchld_handler(int sig);
static void sighup_handler(int sig);
static void sigterm_handler(int sig);
static void usage(void);
int
main(int argc,
char *argv[])
{
int i;
char *opt;
int fg;
fd_set input,
output;
client_t *con;
job_t *job,
*next;
listener_t *lis;
time_t activity;
time_t senddoc_time;
#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
FILE *fp;
#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] == '/')
{
strlcpy(ConfigurationFile, argv[i], sizeof(ConfigurationFile));
}
else
{
getcwd(ConfigurationFile, sizeof(ConfigurationFile));
strlcat(ConfigurationFile, "/", sizeof(ConfigurationFile));
strlcat(ConfigurationFile, argv[i], sizeof(ConfigurationFile));
}
break;
case 'f' :
fg = 1;
break;
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 (!fg)
{
if (fork() > 0)
return (0);
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)
snprintf(TZ, sizeof(TZ), "TZ=%s", getenv("TZ"));
tzset();
getrlimit(RLIMIT_NOFILE, &limit);
if (limit.rlim_max > FD_SETSIZE)
MaxFDs = FD_SETSIZE;
else
MaxFDs = limit.rlim_max;
limit.rlim_cur = MaxFDs;
setrlimit(RLIMIT_NOFILE, &limit);
#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
if (!ReadConfiguration())
{
syslog(LOG_LPR, "Unable to read configuration file \'%s\' - exiting!",
ConfigurationFile);
return (1);
}
#ifdef __sgi
if ((fp = fopen("/var/spool/lp/SCHEDLOCK", "a")) == NULL)
{
syslog(LOG_LPR, "Unable to create fake lpsched lock file "
"\"/var/spool/lp/SCHEDLOCK\"\' - %s!",
strerror(errno));
}
else
{
fclose(fp);
chmod("/var/spool/lp/SCHEDLOCK", 0644);
chown("/var/spool/lp/SCHEDLOCK", User, Group);
}
#endif
InitCerts();
senddoc_time = time(NULL);
#ifdef HAVE_MALLINFO
mallinfo_time = 0;
#endif
for (;;)
{
if (NeedReload)
{
if (NumClients > 0)
{
for (i = NumClients, con = Clients; i > 0; i --, con ++)
if (con->http.state == HTTP_WAITING)
{
CloseClient(con);
con --;
}
else
con->http.keep_alive = HTTP_KEEPALIVE_OFF;
PauseListening();
}
else if (!ReadConfiguration())
{
syslog(LOG_LPR, "Unable to read configuration file \'%s\' - exiting!",
ConfigurationFile);
break;
}
}
input = InputSet;
output = OutputSet;
timeout.tv_sec = 1;
timeout.tv_usec = 0;
for (i = NumClients, con = Clients; i > 0; i --, con ++)
if (con->http.used > 0)
{
timeout.tv_sec = 0;
break;
}
if ((i = select(MaxFDs, &input, &output, 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 (i = 0, con = Clients; i < NumClients; i ++, con ++)
LogMessage(L_EMERG, "Clients[%d] = %d, file = %d, state = %d",
i, 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);
for (job = Jobs; job != NULL; job = job->next)
LogMessage(L_EMERG, "Jobs[%d] = %d", job->id, job->pipe);
break;
}
for (i = NumListeners, lis = Listeners; i > 0; i --, lis ++)
if (FD_ISSET(lis->fd, &input))
AcceptClient(lis);
for (i = NumClients, con = Clients; i > 0; i --, con ++)
{
if (FD_ISSET(con->http.fd, &input) || con->http.used)
if (!ReadClient(con))
{
con --;
continue;
}
if (FD_ISSET(con->http.fd, &output) &&
(!con->pipe_pid || FD_ISSET(con->file, &input)))
if (!WriteClient(con))
{
con --;
continue;
}
activity = time(NULL) - Timeout;
if (con->http.activity < activity && !con->pipe_pid)
{
CloseClient(con);
con --;
continue;
}
}
for (job = Jobs; job != NULL; job = next)
{
next = job->next;
if (job->pipe && FD_ISSET(job->pipe, &input))
{
FD_CLR(job->pipe, &input);
UpdateJob(job);
}
}
if (Browsing && BrowseProtocols)
{
if (BrowseSocket >= 0 && FD_ISSET(BrowseSocket, &input))
UpdateCUPSBrowse();
if (PollPipe >= 0 && FD_ISSET(PollPipe, &input))
UpdatePolling();
#ifdef HAVE_LIBSLP
if ((BrowseProtocols & BROWSE_SLP) && BrowseSLPRefresh <= time(NULL))
UpdateSLPBrowse();
#endif
SendBrowseList();
}
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");
}
}
DeleteAllCerts();
CloseAllClients();
StopListening();
return (1);
}
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
IgnoreChildSignals(void)
{
#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
struct sigaction action;
#endif
#ifdef HAVE_SIGSET
sigset(SIGCHLD, SIG_IGN);
#elif defined(HAVE_SIGACTION)
memset(&action, 0, sizeof(action));
sigemptyset(&action.sa_mask);
sigaddset(&action.sa_mask, SIGCHLD);
action.sa_handler = SIG_IGN;
sigaction(SIGCHLD, &action, NULL);
#else
signal(SIGCLD, SIG_IGN);
#endif
}
static void
sigchld_handler(int sig)
{
int status;
int pid;
job_t *job;
int i;
(void)sig;
#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(("sigcld_handler: pid = %d, status = %d\n", pid, status));
if (pid)
DeleteCert(pid);
if (status == SIGTERM)
status = 0;
if (status)
{
if (status < 256)
LogMessage(L_ERROR, "PID %d crashed on signal %d!", pid, status);
else
LogMessage(L_ERROR, "PID %d stopped with status %d!", pid,
status / 256);
}
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;
}
break;
}
}
}
#ifdef HAVE_SIGSET
sigset(SIGCHLD, sigchld_handler);
#elif !defined(HAVE_SIGACTION)
signal(SIGCLD, sigchld_handler);
#endif
}
static void
sighup_handler(int sig)
{
(void)sig;
NeedReload = TRUE;
#ifdef HAVE_SIGSET
sigset(SIGHUP, sighup_handler);
#elif !defined(HAVE_SIGACTION)
signal(SIGHUP, sighup_handler);
#endif
}
static void
sigterm_handler(int sig)
{
#ifdef __sgi
struct stat statbuf;
#endif
(void)sig;
LogMessage(L_ERROR, "Scheduler shutting down due to SIGTERM.");
CloseAllClients();
StopListening();
StopPolling();
StopBrowsing();
if (Clients != NULL)
free(Clients);
FreeAllJobs();
if (AccessFile != NULL)
fclose(AccessFile);
if (ErrorFile != NULL)
fclose(ErrorFile);
if (PageFile != NULL)
fclose(PageFile);
DeleteAllLocations();
DeleteAllClasses();
if (Devices)
ippDelete(Devices);
if (PPDs)
ippDelete(PPDs);
DeleteAllPrinters();
if (MimeDatabase != NULL)
mimeDelete(MimeDatabase);
#ifdef __sgi
if (!stat("/var/spool/lp/FIFO", &statbuf))
if (!S_ISFIFO(statbuf.st_mode))
unlink("/var/spool/lp/SCHEDLOCK");
#endif
exit(1);
}
static void
usage(void)
{
fputs("Usage: cupsd [-c config-file] [-f]\n", stderr);
exit(1);
}