/* Copyright (c) 2008-2010, Dirk Krause All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above opyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Dirk Krause nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** @file prqd.c The prqd daemon. */ #include "prqd.h" #line 44 "prqd.ctr" /** Program name. */ static char appname[] = { "prqd" }; /** Texts for responses to jobstart requests. */ static char *response_keywords[]= { /* 0 */ "ACCEPT\n", /* 1 */ "FAIL\n", /* 2 */ "HOLD\n", /* 3 */ "REMOVE\n" }; /** Log messages for syslog logging. */ static char *logmsgs[] = { "prqd-acct job u=%s c=%s pr=%s pa=%ld h=%s j=%s", "prqd-acct summary u=%s c=%s pa=%ld l=%s", "prqd-acct summary u=%s c=%s ac=%s", "prqd-acct balance-account u=%s c=%s", "prqd-acct limit-reached u=%s c=%s", }; /** Texts in user information files. */ static char *uift[] = { /* 0 */ "Date: ", /* 1 */ "Printed pages / General limit: ", /* 2 */ "Additional pages in personal account:", /* 3 */ "Allowed to print: ", /* 4 */ "yes", /* 5 */ "no", /* 6 */ "unlimited", NULL }; /** Database entry format for current job. */ static char current_job_format[] = { "%s:%s" }; /** Database key pattern for current job on a printer. */ static char key_current_job_format[] = { "j:%s" }; /** Database key pattern for pages printed by users. */ static char key_user_pages[] = { "p:%s:%s" }; /** Database key pattern for print accounts. */ static char key_user_account[]= { "a:%s:%s" }; /** Empty string. */ static char empty_string[] = { "" }; /** Replace NULL string pointers by pointer to empty string. */ #define XSTR(s) ((s) ? (s) : empty_string) /** Flag: SIGHUP received. */ static #if DK_HAVE_VOLATILE volatile #endif int sighup_r = 0; /** SIGHUP handler. @param signo Signal number (SIGHUP). */ dk_signal_ret_t sighup_handler DK_P1(int, signo) { dksignal_refresh(signo,sighup_handler); sighup_r = 1; dksignal_return(0); } /** Flag: SIGINT received. */ static #if DK_HAVE_VOLATILE volatile #endif int sigint_r = 0; /** SIGINT handler. @param signo Signal number (SIGINT). */ dk_signal_ret_t sigint_handler DK_P1(int, signo) { dksignal_refresh(signo,sigint_handler); sigint_r = 1; dksignal_return(0); } /** Flag: SIGPIPE received. */ static #if DK_HAVE_VOLATILE volatile #endif int sigpipe_r = 0; /** SIGPIPE handler. @param signo Signal number (SIGPIPE). */ dk_signal_ret_t sigpipe_handler DK_P1(int, signo) { dksignal_refresh(signo,sigpipe_handler); sigpipe_r = 1; dksignal_return(0); } /** Flag: SIGTERM was received. */ static #if DK_HAVE_VOLATILE volatile #endif int sigterm_r = 0; /** SIGTERM handler. @param signo Signal number (SIGTERM). */ dk_signal_ret_t sigterm_handler DK_P1(int, signo) { dksignal_refresh(signo,sigterm_handler); sigterm_r = 1; dksignal_return(0); } /** Exit outer loop (main program) only on SIGTERM, SIGINT or serious error. @param pj Pointer to prqd job structure. @return 1 if we can continue, 0 if we have to leave the loop. */ int prqd_cc1 DK_P1(PJ *,pj) { int back = 1; if(sigterm_r) back = 0; if(sigint_r) back = 0; if(pj->e1) back = 0; if(!back) { prqdlog(pj, PRQD_PRIO_INFO, prqd_get_kw(55)); } return back; } /** Exit inner loop on SIGTERM, SIGINT, serious errors or reconfiguration. @param pj Pointer to prqd job structure. @return 1 if we can continue, 0 if we have to leave the loop. */ int prqd_cc2 DK_P1(PJ *,pj) { int back = 0; back = prqd_cc1(pj); if(back) { if(sighup_r) back = 0; /* reconfiguration */ if(pj->e2) back = 0; } if(!back) { prqdlog(pj, PRQD_PRIO_DEBUG, prqd_get_kw(54)); } return back; } /** Check inner ``can continue'' condition. @param pj Pointer to prqd job structure. @return 1 if we can continue, 0 if we need to leave the loop. */ int prqd_cc3 DK_P1(PJ *,pj) { int back = 0; back = prqd_cc1(pj); if(back) { if(sigpipe_r) back = 0; /* broken pipe, exit session */ /* on sighup reconfigure _after_ the session */ if(pj->e2) back = 0; /* but on errors exit loop */ if(pj->e3) back = 0; } if(!back) { prqdlog(pj, PRQD_PRIO_DEBUG, prqd_get_kw(53)); } return back; } /** Change the UID and GID of the process. To minimize the risk of abuse the prqd process should not run in the root account. Use ``run as user ='' and ``run as group ='' in the prqd.conf file to choose a user account and group. @param pj Pointer to prqd job structure. */ static void change_user_and_group DK_P1(PJ *,pj) { struct passwd *ppwd = NULL; struct group *pgrp = NULL; uid_t tuid; gid_t tgid; tuid = geteuid(); tgid = getegid(); if((pj->prqdc)->rag) { pgrp = getgrnam((pj->prqdc)->rag); if(pgrp) { /* gr_gid */ tgid = pgrp->gr_gid; } else { pj->e1 = 1; prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(47), (pj->prqdc)->rag); } } else { prqdlog(pj, PRQD_PRIO_WARNING, prqd_get_kw(49)); } if((pj->prqdc)->rau) { ppwd = getpwnam((pj->prqdc)->rau); if(ppwd) { /* pw_uid */ tuid = ppwd->pw_uid; } else { pj->e1 = 1; prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(48), (pj->prqdc)->rau); } } else { prqdlog(pj, PRQD_PRIO_WARNING, prqd_get_kw(50)); } (void)chown((pj->prqdc)->sockname, tuid, tgid); (void)chown(prqdconf_get_logfile(), tuid, tgid); prqdbe_change_ownership(pj, tuid, tgid); if(pgrp) { setgid(tgid); prqdlog(pj, PRQD_PRIO_INFO, prqd_get_kw(52), (pj->prqdc)->rag); } if(ppwd) { setuid(tuid); prqdlog(pj, PRQD_PRIO_INFO, prqd_get_kw(51), (pj->prqdc)->rau); } } /** For each printer retrieve information about the currently open job from the database. @param pj Pointer to prqd job structure. */ static void initialize_open_jobs DK_P1(PJ *,pj) { PR *pr; dksto_it_reset((pj->prqdc)->pri); while((pr = (PR *)dksto_it_next((pj->prqdc)->pri)) != NULL) { if(!(pr->alt)) { (pr->cj)[0] = '\0'; if((2 + strlen(pr->n)) < pj->sz_dbkey) { sprintf(pj->dbkey, "j:%s", pr->n); if(prqdbe_fetch(pj, pj->dbkey, pj->dbval, pj->sz_dbval)) { if(strlen(pj->dbval) < (pr->sz_cj)) { strcpy(pr->cj, pj->dbval); } else { prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(46), pj->dbval); } } else { prqdbe_store(pj, pj->dbkey, pr->cj); } } else { prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(36), pr->n); } } } } /** Read a request from standard input to buffer. @param pj Pointer to prqd job structure. @return 1 on success, 0 on error. */ static int read_request DK_P1(PJ *,pj) { int back = 0; size_t sz = 0; fd_set rfds; struct timeval tv; if(((pj->prqdc)->to_seconds > 0L) ||((pj->prqdc)->to_microseconds > 0L)) { prqdlog(pj, PRQD_PRIO_DEBUG, prqd_get_kw(17)); FD_ZERO(&rfds); FD_SET(pj->ss, &rfds); tv.tv_sec = (pj->prqdc)->to_seconds; tv.tv_usec = (pj->prqdc)->to_microseconds; if(select((1 + pj->ss), &rfds, NULL, NULL, &tv) > 0) { if(FD_ISSET(pj->ss,&rfds)) { sz = recv(pj->ss, pj->b1, pj->sz_b1, 0); } } } else { prqdlog(pj, PRQD_PRIO_DEBUG, prqd_get_kw(18)); sz = recv(pj->ss, pj->b1, pj->sz_b1, 0); } if(sz > 0) { prqdlog(pj, PRQD_PRIO_DEBUG, prqd_get_kw(19), (unsigned long)sz); if(sz >= pj->sz_b1) { (pj->b1)[(pj->sz_b1) - 1] = '\0'; } else { (pj->b1)[sz] = '\0'; } back = 1; } else { prqdlog(pj, PRQD_PRIO_DEBUG, prqd_get_kw(20)); } return back; } /** Write user information file for a user on a printer class. @param pj Pointer to prqd job structure. @param uname User name. @param pclass Printer class name. */ void prqd_write_userinfo_file DK_P3(PJ *,pj, char *,uname, char *,pclass) { PC *pc; LE *le; long uap = 0L; long upp = 0L; long l; char keybuffer[PRQD_DBENTRY_SIZE], valbuffer[PRQD_DBENTRY_SIZE], pagebuffer[32]; int limit_value; unsigned long limit_pages; int is_bigger = 0, apply_limit = 0, is_allowed = 0; struct group *gr; struct passwd *pw; uid_t uid_uname; gid_t gid_uname; char *fn, *p1, **lfdptr; time_t timer; struct tm *tm; FILE *fipo; size_t sz; int do_print = 0; if((pj) && (uname) && (pclass)) { if((pj->prqdc)->userinfdir) { if((3 + strlen(uname) + strlen(pclass)) < sizeof(keybuffer)) { /* find printed pages */ sprintf(keybuffer, "p:%s:%s", pclass, uname); if(prqdbe_fetch(pj, keybuffer, valbuffer, sizeof(valbuffer))) { if(sscanf(valbuffer, "%ld", &l) == 1) { upp = l; } } /* find pages in personal account */ sprintf(keybuffer, "a:%s:%s", pclass, uname); if(prqdbe_fetch(pj, keybuffer, valbuffer, sizeof(valbuffer))) { if(sscanf(valbuffer, "%ld", &l) == 1) { uap = l; } } /* find limit */ limit_value = LIMIT_VALUE_DENIED; limit_pages = 0UL; is_allowed = 0; pc = (PC *)dksto_it_find_like((pj->prqdc)->cli, pclass, 1); if(pc) { if(pc->wi) { do_print = 1; uid_uname = 0; gid_uname = 0; pw = getpwnam(uname); if(pw) { uid_uname = pw->pw_uid; gid_uname = pw->pw_gid; } else { prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(45), uname); } dksto_it_reset(pc->li); while((le = dksto_it_next(pc->li)) != NULL) { is_bigger = 0; if(le->val > limit_value) { is_bigger = 1; } else { if((le->val == LIMIT_VALUE_PAGES) && (limit_value == LIMIT_VALUE_PAGES)) { if(le->p > limit_pages) { is_bigger = 1; } } } if(is_bigger) { apply_limit = 0; switch(le->tp) { case LIMIT_ENTRY_TYPE_DEFAULT: { apply_limit = 1; } break; case LIMIT_ENTRY_TYPE_GROUP: { if(le->who) { gr = getgrnam(le->who); if(gr) { if(pw) { if(gid_uname == gr->gr_gid) { apply_limit = 1; } } if(!apply_limit) { lfdptr = gr->gr_mem; while((*lfdptr) && (apply_limit == 0)) { if(strcmp(*lfdptr, uname) == 0) { apply_limit = 1; } lfdptr++; } } } } } break; case LIMIT_ENTRY_TYPE_USER: { if(le->who) { if(strcmp(le->who, uname) == 0) { apply_limit = 1; } } } break; } if(apply_limit) { limit_value = le->val; limit_pages = le->p; } } } } } else { prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(44), pclass); } /* decide */ switch(limit_value) { case LIMIT_VALUE_PAGES: { if(limit_pages > upp) { is_allowed = 1; } else { if(uap > 0L) { is_allowed = 1; } } } break; case LIMIT_VALUE_UNLIMITED: { is_allowed = 1; } break; } /* write output */ if(do_print) { sz = 7 + strlen((pj->prqdc)->userinfdir) + strlen(uname) + strlen(pclass); fn = dk_new(char,sz); if(fn) { time(&timer); tm = localtime(&timer); sprintf(fn, "%s/%s-%s.txt", (pj->prqdc)->userinfdir, pclass, uname); fipo = dksf_fopen(fn, "w"); if(fipo) { fchmod(fileno(fipo), (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH)); fprintf(fipo, "%s %04d-%02d-%02d %02d:%02d:%02d\r\n", uift[0], (1900 + tm->tm_year), (1 + tm->tm_mon), tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec ); switch(limit_value) { case LIMIT_VALUE_UNLIMITED: { p1 = uift[6]; } break; case LIMIT_VALUE_PAGES: { sprintf(pagebuffer, "%lu", limit_pages); p1 = pagebuffer; } break; default: { sprintf(pagebuffer, "%lu", 0UL); p1 = pagebuffer; } break; } fprintf(fipo, "%s %ld / %s\r\n", uift[1], upp, p1); fprintf(fipo, "%s %ld\r\n", uift[2], uap); fprintf(fipo, "%s %s\r\n", uift[3], uift[is_allowed ? 4 : 5]); fclose(fipo); fipo = NULL; } else { prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(43), fn); } chmod(fn, 0644); dk_delete(fn); } else { prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(42)); } } } else { prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(41), uname, pclass); } } } else { prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(40)); } } /** Process jobstart request. Check whether the user is allowed to print. @param pj Pointer to prqd job structure. @return 1 on success, 0 on error. */ static int jobstart_request DK_P1(PJ *,pj) { int back = 0; long upp = 0L, uap = 0L; prqd_pj_get_names(pj); prqd_pj_find_limit(pj); if((3 + strlen(pj->cname) + strlen(pj->uname)) < pj->sz_dbkey) { sprintf(pj->dbkey, key_user_pages, pj->cname, pj->uname); if(prqdbe_fetch(pj, pj->dbkey, pj->dbval, pj->sz_dbval)) { if(sscanf(pj->dbval, "%ld", &upp) != 1) { upp = 0L; back = 0; prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(37), pj->dbval); } } } else { back = 0; } if((3 + strlen(pj->cname) + strlen(pj->uname)) < pj->sz_dbkey) { sprintf(pj->dbkey, key_user_account, pj->cname, pj->uname); if(prqdbe_fetch(pj, pj->dbkey, pj->dbval, pj->sz_dbval)) { if(sscanf(pj->dbval, "%ld", &uap) != 1) { uap = 0L; back = 0; prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(38), pj->dbval); } } } else { back = 0; } switch(pj->limit_type) { case LIMIT_VALUE_UNLIMITED: { strcpy(pj->b2, response_keywords[0]); back = 1; } break; case LIMIT_VALUE_PAGES: { if((unsigned long)upp < pj->limit_pages) { strcpy(pj->b2, response_keywords[0]); back = 1; } else { if(uap > 0L) { strcpy(pj->b2, response_keywords[0]); back = 1; } else { } } } break; default: { } break; } prqdlog(pj, PRQD_PRIO_INFO, prqd_get_kw(39), back); return back; } /** Seed pseudo random number generator for faked limits. For users which do not exist we return a faked entry indicating ``over limit'' to prevent prqd for being abused to guess user names. */ static void set_random_for_name DK_P1(char *,uname) { char *p1, *p2; size_t szrs, i; unsigned rs; rs = (unsigned)getpid; p1 = (char *)(&rs); szrs = sizeof(unsigned); p2 = uname; i = 0; while(*p2) { p1[i] = p1[i] ^ (*p2); p2++; i++; if(i >= szrs) { i = 0; } } srand(rs); } /** Process info request. @param pj Pointer to prqd job structure. @param p Additional arguments for the request. @return 1 on success, 0 on error. */ static int info_request DK_P2(PJ *,pj, char *,p) { int back = 1; char *p1, *p2; long upp = 0L, uap = 0L, page_limit = 0L; struct passwd *pw; /* uppt: bereits gedruckte Seiten uapt: Seiten in persoenlichem Account lt: Limit (Seitenzahl) ft: Flag (0 oder 1) */ char uppt[32], uapt[32], lt[32], ft[32]; sprintf(uppt, "%ld", 0L); sprintf(uapt, "%ld", 0L); sprintf(lt, "%ld", 0L); sprintf(ft, "%ld", 0L); if(p) { p1 = dkstr_start(p, NULL); if(p1) { p2 = dkstr_next(p1, NULL); if(p2) { pw = getpwnam(p2); if(pw) { prqd_pj_set_arg(pj, 'n', p2); prqd_pj_set_arg(pj, 'P', p1); prqd_pj_get_names(pj); prqd_pj_find_limit(pj); if(pj->cname) { if((3 + strlen(pj->cname) + strlen(pj->uname)) < pj->sz_dbkey) { sprintf(pj->dbkey, key_user_pages, pj->cname, pj->uname); if(prqdbe_fetch(pj, pj->dbkey, pj->dbval, pj->sz_dbval)) { if(sscanf(pj->dbval, "%ld", &upp) != 1) { upp = 0L; back = 0; prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(37), pj->dbval); } else { sprintf(uppt, "%ld", upp); } } } else { back = 0; } if((3 + strlen(pj->cname) + strlen(pj->uname)) < pj->sz_dbkey) { sprintf(pj->dbkey, key_user_account, pj->cname, pj->uname); if(prqdbe_fetch(pj, pj->dbkey, pj->dbval, pj->sz_dbval)) { if(sscanf(pj->dbval, "%ld", &uap) != 1) { uap = 0L; back = 0; prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(38), pj->dbval); } else { sprintf(uapt, "%ld", uap); } } } else { back = 0; } switch(pj->limit_type) { case LIMIT_VALUE_UNLIMITED: { /* strcpy(pj->b2, response_keywords[0]); */ sprintf(ft, "%ld", 1L); sprintf(lt, "%ld", -1L); back = 1; } break; case LIMIT_VALUE_PAGES: { sprintf(lt, "%ld", pj->limit_pages); if((unsigned long)upp < pj->limit_pages) { /* strcpy(pj->b2, response_keywords[0]); */ sprintf(ft, "%ld", 1L); back = 1; } else { if(uap > 0L) { /* strcpy(pj->b2, response_keywords[0]); */ sprintf(ft, "%ld", 1L); back = 1; } else { } } } break; default: { } break; } } else { /* ##### ERROR: Missing class name */ } } else { /* ##### Invalid user name. Provide faked data */ prqd_pj_set_arg(pj, 'n', p2); prqd_pj_set_arg(pj, 'P', p1); prqd_pj_get_names(pj); prqd_pj_find_limit(pj); if(pj->cname) { page_limit = 200L; set_random_for_name(pj->uname); upp = page_limit + (rand() % page_limit); uap = 0 - (rand() % (2 * page_limit)); sprintf(uppt, "%ld", upp); sprintf(uapt, "%ld", uap); sprintf(lt, "%ld", page_limit); } else { /* ##### ERROR: Missing class name */ } } } else { /* ##### ERROR: Missing user name */ } } else { /* ##### ERROR: Missing printer name */ } } sprintf(pj->b2, "%s %s %s %s\n", lt, uppt, uapt, ft); return back; } /** Process file start request. Keep track of the current print user and the old pagecount value of a printer. @param pj Pointer to prqd job structure. @return 1 on success, 0 on error. */ static int filestart_request DK_P1(PJ *,pj) { int back = 1; PR *pr; #if OLD_VERSION if((pj->uname) && (pj->pname) && (pj->pages)) { pc = NULL; pr = NULL; pr = dksto_it_find_like((pj->prqdc)->pri, pj->pname, 1); if(pr) { while(pr->alt) { pr = pr->alt; } if(pr->pc) { pc = pr->pc; if((1 + strlen(pj->pages) + strlen(pj->uname)) < pr->sz_cj) { sprintf(pr->cj, "%s:%s", pj->pages, pj->uname); if((2 + strlen(pr->n)) < pj->sz_dbkey) { sprintf(pj->dbkey, "j:%s", pr->n); prqdbe_store(pj, pj->dbkey, pr->cj); } } else { } } else { } } else { } } #else prqd_pj_get_names(pj); if((pj->uname) && (pj->pname) && (pj->pages) && (pj->rname) && (pj->cname)) { pr = dksto_it_find_like((pj->prqdc)->pri, pj->rname, 1); if(pr) { if((1 + strlen(pj->pages) + strlen(pj->uname)) < pr->sz_cj) { sprintf(pr->cj, current_job_format, pj->pages, pj->uname); if((2 + strlen(pr->n)) < pj->sz_dbkey) { sprintf(pj->dbkey, key_current_job_format, pr->n); if(!prqdbe_store(pj, pj->dbkey, pr->cj)) { back = 0; } } else { prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(36), pr->n); back = 0; } } else { prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(36), pj->uname); back = 0; } } else { prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(26), pj->rname); back = 0; } } else { prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(25)); } #endif return back; } /** Buffer for print limit text. */ static char limit_buffer[32]; /** Keywords used in limitations. */ static char *limit_keywords[] = { "denied", "unlimited" }; /** Convert limit value back to text form. @param pj Pointer to prqd job structure. @return Pointer to text form of limit. */ static char *limit_str DK_P1(PJ *,pj) { char *back; back = limit_keywords[0]; switch(pj->limit_type) { case LIMIT_VALUE_UNLIMITED: { back = limit_keywords[1]; } break; default: { sprintf(limit_buffer, "%lu", pj->limit_pages); back = limit_buffer; } break; } return back; } /** Handle fileend request. Calculate number of pages in the job and process and change database entries. @param pj Pointer to print job data. @return 1 on success, 0 on error. */ #if VERSION_BEFORE_2010_10_26 static int fileend_request DK_P1(PJ *,pj) { int back = 1; PR *pr; unsigned long job_start_pages, job_end_pages; long job_pages; long upp; /* user pages printed */ long uap; /* user account of pages */ char *ou; /* old user name (start of job) */ int must_balance; #if OLD_VERSION if((pj->uname) && (pj->pname) && (pj->pages)) { prqd_pj_find_limit(pj); pc = NULL; pr = NULL; pr = dksto_it_find_like((pj->prqdc)->pri, pj->pname, 1); if(pr) { while(pr->alt) { pr = pr->alt; } if(pr->pc) { pc = pr->pc; if(pc) { } } } } #else job_start_pages = job_end_pages = 0UL; job_pages = 0L; upp = uap = 0L; ou = NULL; prqd_pj_get_names(pj); prqd_pj_find_limit(pj); if((pj->uname) && (pj->pname) && (pj->pages) && (pj->rname) && (pj->cname)) { pr = dksto_it_find_like((pj->prqdc)->pri, pj->rname, 1); if(pr) { ou = dkstr_chr(pr->cj, ':'); if(ou) { *(ou++) = '\0'; if(strcmp(ou, pj->uname) == 0) { if(sscanf(pr->cj, "%lu", &job_start_pages) == 1) { if(sscanf(pj->pages, "%lu", &job_end_pages) == 1) { if(job_end_pages > job_start_pages) { job_pages = (long)(job_end_pages - job_start_pages); if((3 + strlen(pj->cname) + strlen(pj->uname)) < pj->sz_dbkey) { sprintf(pj->dbkey, key_user_pages, pj->cname, pj->uname); if(prqdbe_fetch(pj, pj->dbkey, pj->dbval, pj->sz_dbval)) { if(sscanf(pj->dbval, "%ld", &upp) != 1) { upp = 0L; back = 0; } } } else { back = 0; } if((3 + strlen(pj->cname) + strlen(pj->uname)) < pj->sz_dbkey) { sprintf(pj->dbkey, key_user_account, pj->cname, pj->uname); if(prqdbe_fetch(pj, pj->dbkey, pj->dbval, pj->sz_dbval)) { if(sscanf(pj->dbval, "%ld", &uap) != 1) { uap = 0L; back = 0; } } } else { back = 0; } upp = upp + job_pages; if((3 + strlen(pj->cname) + strlen(pj->uname)) < pj->sz_dbkey) { sprintf(pj->dbkey, key_user_pages, pj->cname, pj->uname); sprintf(pj->dbval, "%ld", upp); if(!prqdbe_store(pj, pj->dbkey, pj->dbval)) { back = 0; } } /* ##### LOG job summary */ prqdlog(pj,PRQD_PRIO_INFO,logmsgs[0],pj->uname,pj->cname,pj->pname,job_pages,XSTR(pj->hname),XSTR(pj->tname)); prqdlog(pj,PRQD_PRIO_INFO,logmsgs[1],pj->uname,pj->cname,upp,limit_str(pj)); must_balance = 0; if(pj->limit_type == LIMIT_VALUE_PAGES) { if(upp > pj->limit_pages) { must_balance = 1; } } if(must_balance) { uap = uap + (long)(pj->limit_pages) - upp; upp = pj->limit_pages; if((3 + strlen(pj->cname) + strlen(pj->uname)) < pj->sz_dbkey) { sprintf(pj->dbkey, key_user_pages, pj->cname, pj->uname); sprintf(pj->dbval, "%ld", upp); if(!prqdbe_store(pj, pj->dbkey, pj->dbval)) { back = 0; } } if((3 + strlen(pj->cname) + strlen(pj->uname)) < pj->sz_dbkey) { sprintf(pj->dbkey, key_user_account, pj->cname, pj->uname); sprintf(pj->dbval, "%ld", uap); if(!prqdbe_store(pj, pj->dbkey, pj->dbval)) { back = 0; } } prqdlog(pj, PRQD_PRIO_INFO, logmsgs[3],pj->uname,pj->cname); prqdlog(pj,PRQD_PRIO_INFO,logmsgs[1],pj->uname,pj->cname,upp,limit_str(pj)); prqdlog(pj,PRQD_PRIO_INFO,logmsgs[2],pj->uname,pj->cname,pj->dbval); } } else { prqdlog(pj,PRQD_PRIO_ERROR,prqd_get_kw(34)); back = 0; } } else { prqdlog(pj,PRQD_PRIO_ERROR,prqd_get_kw(33), pj->pages); back = 0; } } else { prqdlog(pj,PRQD_PRIO_ERROR,prqd_get_kw(32), pr->cj); back = 0; } } else { prqdlog(pj,PRQD_PRIO_ERROR,prqd_get_kw(31), ou, pj->uname); } } else { prqdlog(pj,PRQD_PRIO_ERROR,prqd_get_kw(30)); } (pr->cj)[0] = '\0'; if((2 + strlen(pr->n)) < pj->sz_dbkey) { sprintf(pj->dbkey, key_current_job_format, pr->n); prqdbe_store(pj, pj->dbkey, pr->cj); } } else { prqdlog(pj,PRQD_PRIO_ERROR,prqd_get_kw(26), pj->rname); back = 0; } prqd_write_userinfo_file(pj, pj->uname, pj->cname); } else { prqdlog(pj,PRQD_PRIO_ERROR,prqd_get_kw(25)); back = 0; } #endif return back; } #else static int fileend_request DK_P1(PJ *,pj) { int back = 1; PR *pr; unsigned long job_start_pages, job_end_pages; long job_pages; long upp; /* user pages printed */ long uap; /* user account of pages */ char *ou; /* old user name (start of job) */ int must_balance; int have_account; #if OLD_VERSION if((pj->uname) && (pj->pname) && (pj->pages)) { prqd_pj_find_limit(pj); pc = NULL; pr = NULL; pr = dksto_it_find_like((pj->prqdc)->pri, pj->pname, 1); if(pr) { while(pr->alt) { pr = pr->alt; } if(pr->pc) { pc = pr->pc; if(pc) { } } } } #else job_start_pages = job_end_pages = 0UL; job_pages = 0L; upp = uap = 0L; ou = NULL; have_account = 0; prqd_pj_get_names(pj); prqd_pj_find_limit(pj); if((pj->uname) && (pj->pname) && (pj->pages) && (pj->rname) && (pj->cname)) { pr = dksto_it_find_like((pj->prqdc)->pri, pj->rname, 1); if(pr) { ou = dkstr_chr(pr->cj, ':'); if(ou) { *(ou++) = '\0'; if(strcmp(ou, pj->uname) == 0) { if(sscanf(pr->cj, "%lu", &job_start_pages) == 1) { if(sscanf(pj->pages, "%lu", &job_end_pages) == 1) { if(job_end_pages > job_start_pages) { job_pages = (long)(job_end_pages - job_start_pages); if((3 + strlen(pj->cname) + strlen(pj->uname)) < pj->sz_dbkey) { sprintf(pj->dbkey, key_user_pages, pj->cname, pj->uname); if(prqdbe_fetch(pj, pj->dbkey, pj->dbval, pj->sz_dbval)) { if(sscanf(pj->dbval, "%ld", &upp) != 1) { upp = 0L; back = 0; } } } else { back = 0; } if((3 + strlen(pj->cname) + strlen(pj->uname)) < pj->sz_dbkey) { sprintf(pj->dbkey, key_user_account, pj->cname, pj->uname); if(prqdbe_fetch(pj, pj->dbkey, pj->dbval, pj->sz_dbval)) { have_account = 1; if(sscanf(pj->dbval, "%ld", &uap) != 1) { uap = 0L; back = 0; have_account = 0; } } } else { back = 0; } upp = upp + job_pages; /* ##### LOG job summary */ prqdlog(pj,PRQD_PRIO_INFO,logmsgs[0],pj->uname,pj->cname,pj->pname,job_pages,XSTR(pj->hname),XSTR(pj->tname)); prqdlog(pj,PRQD_PRIO_INFO,logmsgs[1],pj->uname,pj->cname,upp,limit_str(pj)); must_balance = 0; if(pj->limit_type == LIMIT_VALUE_PAGES) { if(upp > pj->limit_pages) { must_balance = 1; } } if(must_balance) { if(have_account || (pj->prqdc)->do_balance) { uap = uap + (long)(pj->limit_pages) - upp; upp = pj->limit_pages; if((3 + strlen(pj->cname) + strlen(pj->uname)) < pj->sz_dbkey) { sprintf(pj->dbkey, key_user_pages, pj->cname, pj->uname); sprintf(pj->dbval, "%ld", upp); if(!prqdbe_store(pj, pj->dbkey, pj->dbval)) { back = 0; } } if((3 + strlen(pj->cname) + strlen(pj->uname)) < pj->sz_dbkey) { sprintf(pj->dbkey, key_user_account, pj->cname, pj->uname); sprintf(pj->dbval, "%ld", uap); if(!prqdbe_store(pj, pj->dbkey, pj->dbval)) { back = 0; } } prqdlog(pj, PRQD_PRIO_INFO, logmsgs[3],pj->uname,pj->cname); prqdlog(pj,PRQD_PRIO_INFO,logmsgs[1],pj->uname,pj->cname,upp,limit_str(pj)); prqdlog(pj,PRQD_PRIO_INFO,logmsgs[2],pj->uname,pj->cname,pj->dbval); } else { upp = pj->limit_pages; if((3 + strlen(pj->cname) + strlen(pj->uname)) < pj->sz_dbkey) { sprintf(pj->dbkey, key_user_pages, pj->cname, pj->uname); sprintf(pj->dbval, "%ld", upp); if(!prqdbe_store(pj, pj->dbkey, pj->dbval)) { back = 0; } } prqdlog(pj, PRQD_PRIO_INFO, logmsgs[4],pj->uname,pj->cname); prqdlog(pj,PRQD_PRIO_INFO,logmsgs[1],pj->uname,pj->cname,upp,limit_str(pj)); } } else { if((3 + strlen(pj->cname) + strlen(pj->uname)) < pj->sz_dbkey) { sprintf(pj->dbkey, key_user_pages, pj->cname, pj->uname); sprintf(pj->dbval, "%ld", upp); if(!prqdbe_store(pj, pj->dbkey, pj->dbval)) { back = 0; } } } } else { prqdlog(pj,PRQD_PRIO_ERROR,prqd_get_kw(34)); back = 0; } } else { prqdlog(pj,PRQD_PRIO_ERROR,prqd_get_kw(33), pj->pages); back = 0; } } else { prqdlog(pj,PRQD_PRIO_ERROR,prqd_get_kw(32), pr->cj); back = 0; } } else { prqdlog(pj,PRQD_PRIO_ERROR,prqd_get_kw(31), ou, pj->uname); } } else { prqdlog(pj,PRQD_PRIO_ERROR,prqd_get_kw(30)); } (pr->cj)[0] = '\0'; if((2 + strlen(pr->n)) < pj->sz_dbkey) { sprintf(pj->dbkey, key_current_job_format, pr->n); prqdbe_store(pj, pj->dbkey, pr->cj); } } else { prqdlog(pj,PRQD_PRIO_ERROR,prqd_get_kw(26), pj->rname); back = 0; } prqd_write_userinfo_file(pj, pj->uname, pj->cname); } else { prqdlog(pj,PRQD_PRIO_ERROR,prqd_get_kw(25)); back = 0; } #endif return back; } #endif /** Handle jobend request. No action necessary. @param pj Pointer to prqd job structure. @return 1 on success, 0 on error. */ static int jobend_request DK_P1(PJ *,pj) { int back = 1; return back; } /** Find request type and start dedicated handler function. @param pj Pointer to print job data. @return 1 on success, 0 on error. */ static int process_request DK_P1(PJ *,pj) { int back = 0; char *p1, *p2; pj->rqtstr = p1 = dkstr_start(pj->b1, NULL); if(p1) { dkstr_chomp(p1, NULL); prqdlog(pj,PRQD_PRIO_INFO,"%s",p1); p2 = dkstr_next(p1, NULL); prqd_pj_get_rq_type(pj, p1); pj->uname = NULL; pj->pname = NULL; pj->tname = NULL; pj->pages = NULL; pj->cname = NULL; pj->rname = NULL; pj->hname = NULL; switch(pj->rqt) { case PRQD_RQ_T_JOBSTART: { pj->must_respond = 0x01; prqd_pj_set_all_args(pj, p2); if(!jobstart_request(pj)) { prqd_pj_use_deny_action(pj); } back = 1; } break; case PRQD_RQ_T_FILESTART: case PRQD_RQ_T_START: { pj->must_respond = 0x00; prqd_pj_set_all_args(pj, p2); back = filestart_request(pj); } break; case PRQD_RQ_T_END: case PRQD_RQ_T_FILEEND: { pj->must_respond = 0x00; prqd_pj_set_all_args(pj, p2); back = fileend_request(pj); } break; case PRQD_RQ_T_JOBEND: { pj->must_respond = 0x00; prqd_pj_set_all_args(pj, p2); back = jobend_request(pj); } break; case PRQD_RQ_T_CONTROL: { back = prqdctrl_request(pj, p2); } break; case PRQD_RQ_T_INFO: { pj->must_respond = 0x01; back = info_request(pj, p2); } break; default: { } break; } } else { } return back; } /** Send response text to peer if necessary. @param pj Pointer to print job data. @return 1 on success, 0 on error. */ static int write_response DK_P1(PJ *,pj) { int back = 0; size_t sz, osz; char logbuffer[64]; fd_set wfds; struct timeval tv; if(pj->must_respond) { if(strlen(pj->b2) < sizeof(logbuffer)) { strcpy(logbuffer, pj->b2); dkstr_chomp(logbuffer, NULL); prqdlog(pj,PRQD_PRIO_INFO,"Response: %s",logbuffer); } sz = 1 + strlen(pj->b2); if(((pj->prqdc)->to_seconds > 0L) ||((pj->prqdc)->to_microseconds > 0L)) { FD_ZERO(&wfds); FD_SET(pj->ss,&wfds); tv.tv_sec = (pj->prqdc)->to_seconds; tv.tv_usec = (pj->prqdc)->to_microseconds; if(select((1 + pj->ss), NULL, &wfds, NULL, &tv) > 0) { if(FD_ISSET(pj->ss,&wfds)) { osz = send(pj->ss, pj->b2, sz, 0); if(osz >= sz) { back = 1; } } } } else { osz = send(pj->ss, pj->b2, sz, 0); if(osz >= sz) { back = 1; } } } else { back = 1; } return back; } /** Run a service session. As long as the peer sends request data process the requests and send the response. @param pj Pointer to print job data. */ static void run_session DK_P1(PJ *,pj) { int cc; cc = 1; sigpipe_r = 0; pj->e3 = 0; while((cc) && prqd_cc3(pj)) { /* (pj->b2)[0] = '\0'; */ strcpy(pj->b2, response_keywords[3]); pj->must_respond = 0x00; prqdlog(pj, PRQD_PRIO_DEBUG, prqd_get_kw(15)); if(read_request(pj)) { prqdlog(pj, PRQD_PRIO_DEBUG, prqd_get_kw(16)); if(!process_request(pj)) { cc = 0; } prqdlog(pj, PRQD_PRIO_DEBUG, prqd_get_kw(21)); write_response(pj); } else { cc = 0; } } } /** Accept connection requests and start a session as long as we can continue. @param pj Pointer to print job structure. */ static void inner_loop DK_P1(PJ *,pj) { sighup_r = 0; pj->e2 = 0; while(prqd_cc2(pj)) { prqdlog(pj, PRQD_PRIO_DEBUG, prqd_get_kw(10)); pj->ss = accept(pj->sock, NULL, 0); if(pj->ss > -1) { prqdlog(pj, PRQD_PRIO_DEBUG, prqd_get_kw(11)); run_session(pj); prqdlog(pj, PRQD_PRIO_DEBUG, prqd_get_kw(12)); close(pj->ss); pj->ss = -1; prqdlog(pj, PRQD_PRIO_DEBUG, prqd_get_kw(13)); } else { if(errno != EINTR) { prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(14)); pj->e1 = 1; } } } } /** Create and set up socket and run service loop. @param pj Pointer to print job structure. */ static void outer_loop DK_P1(PJ *,pj) { static int first_run = 1; struct sockaddr_un soun; char *sn; sn = (pj->prqdc)->sockname; unlink(sn); pj->sock = socket(PF_UNIX, SOCK_STREAM, 0); if(pj->sock > -1) { soun.sun_family = AF_UNIX; if(strlen(sn) < 108) { strcpy(soun.sun_path, sn); if(bind(pj->sock, (struct sockaddr *)(&soun), SZSOUN) == 0) { if(listen(pj->sock, pj->backlog) == 0) { chmod(sn, 0660); if(first_run) { first_run = 0; change_user_and_group(pj); } prqdlog(pj, PRQD_PRIO_PROGRESS, prqd_get_kw(8)); initialize_open_jobs(pj); prqdlog(pj, PRQD_PRIO_PROGRESS, prqd_get_kw(9)); inner_loop(pj); } else { prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(7), sn); pj->e1 = 1; } } else { prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(6), sn); pj->e1 = 1; } } else { prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(5), sn); pj->e1 = 1; } } else { prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(4)); pj->e1 = 1; } } /** Run the service in a loop as long as we can continue (no SIGTERM received or fatal error occured). On SIGHUP reload the configuration file. @param pj Pointer to print job structure. */ static void run_the_service DK_P1(PJ *,pj) { #line 1501 "prqd.ctr" while(prqd_cc1(pj)) { sighup_r = 0; pj->e2 = 0; pj->prqdc = prqdconf_new_prqdc(pj, prqdconf_get_cfgfile()); if(pj->prqdc) { if(prqdbe_open(pj)) { if((pj->prqdc)->sockname) { outer_loop(pj); } else { prqdlog(pj,PRQD_PRIO_ERROR,prqd_get_kw(24)); pj->e1 = 1; } prqdbe_close(pj); } else { prqdlog(pj,PRQD_PRIO_ERROR,prqd_get_kw(23)); pj->e1 = 1; } prqdconf_delete_prqdc(pj->prqdc); pj->prqdc = NULL; } else { prqdlog(pj,PRQD_PRIO_ERROR,prqd_get_kw(22)); pj->e1 = 1; } } #line 1526 "prqd.ctr" } /** Run processing. Change to root directory, set file creation mask, close all files and detach from terminals before running the service in background. @param pj Pointer to print job structure. */ static void run DK_P1(PJ *,pj) { int maxfiles, i; pid_t pid; dk_signal_disp_t disp_hup; dk_signal_disp_t disp_int; dk_signal_disp_t disp_term; dk_signal_disp_t disp_pipe; disp_hup = disp_int = disp_term = disp_pipe = NULL; (void)chdir("/"); umask(077); maxfiles = (int)dksf_get_maxfiles(); for(i = 0; i < maxfiles; i++) { (void)close(i); } pid = fork(); if(pid == 0) { #if DK_HAVE_SETSID setsid(); #else #if DK_HAVE_SETPGRP setpgrp(); #endif #endif disp_hup = dksignal_set(SIGHUP, sighup_handler); pid = fork(); if(pid == 0) { dksf_write_pid_file(appname, 1); disp_int = dksignal_set(SIGINT,sigint_handler); disp_term = dksignal_set(SIGTERM,sigterm_handler); disp_pipe = dksignal_set(SIGPIPE,sigpipe_handler); #if DK_HAVE_SYSLOG_H openlog(prqdconf_get_progname(),(LOG_CONS|LOG_NDELAY|LOG_PID),LOG_LPR); #endif prqdlog(pj, PRQD_PRIO_INFO, "%s", prqd_get_kw(1)); prqdlog(pj, PRQD_PRIO_INFO, "%s", prqd_get_kw(3)); run_the_service(pj); prqdlog(pj, PRQD_PRIO_INFO, "%s", prqd_get_kw(2)); #if DK_HAVE_SYSLOG_H closelog(); #endif if(disp_pipe) { dksignal_set(SIGPIPE,disp_pipe); } if(disp_term) { dksignal_set(SIGTERM,disp_term); } if(disp_int) { dksignal_set(SIGINT,disp_int); } dksf_write_pid_file(appname, 0); } else { if(pid == ((pid_t)-1)) { pj->ec = 1; } } dksignal_set(SIGHUP, disp_hup); } else { if(pid == ((pid_t)-1)) { pj->ec = 1; } } } /** Buffer to read input line. */ static char inbuffer[PRQD_BUFFER_SIZE]; /** Buffer to create a response. */ static char outbuffer[PRQD_BUFFER_SIZE]; /** Buffer for use as b1 in static PJ. */ static char dbkey[PRQD_DBENTRY_SIZE]; /** Buffer for use as b2 in static PJ. */ static char dbval[PRQD_DBENTRY_SIZE]; /** Buffer for use as b3 in static PJ. */ static char pgnamebuffer[PRQD_DBENTRY_SIZE]; /** Array of argument pointers for use in static PJ. */ static char *args[53]; /** Main function. This function initializes a static PJ structure, and sets up buffers before running further processing. @param argc Number of command line arguments. @param argv Command line arguments array. @return 0 on success, positive values on error. */ #if DK_HAVE_PROTOTYPES int main(int argc, char *argv[]) #else int main(argc, argv) int argc; char *argv[]; #endif { PJ pj; prqd_pj_init(&pj); pj.b1 = inbuffer; pj.sz_b1 = sizeof(inbuffer); pj.b2 = outbuffer; pj.sz_b2 = sizeof(outbuffer); pj.b3 = pgnamebuffer; pj.sz_b3 = sizeof(pgnamebuffer); pj.args = args; pj.dbkey = dbkey; pj.sz_dbkey = sizeof(dbkey); pj.dbval = dbval; pj.sz_dbval = sizeof(dbval); pj.allow_file_logging = 1; run(&pj); exit(pj.ec); return(pj.ec); }