/* 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 prqdcl.c Prqd client. */ /** Inside the prqdcl module. */ #define PRQDCL_C 1 #include "prqd.h" #line 45 "prqdcl.ctr" /** Default string to use if a string is missing. */ static char default_string[] = { "***" }; /** Use default string instead of NULL. */ #define XSTR(s) ((s) ? (s) : default_string) /** Retrieve short host name part from fully qualified host name. @param hn Full host name. @return Pointer to short host name on success, NULL on error. */ static char * short_host_name DK_P1(char *,hn) { char *back = NULL; static char mybuffer[32], *ptr; size_t l; if(hn) { back = hn; l = strlen(hn); if(l > 32) l = 32; strncpy(mybuffer, hn, l); if(l > 31) l = 31; mybuffer[l] = '\0'; ptr = strchr(mybuffer, '.'); if(ptr) { *ptr = '\0'; back = mybuffer; } } return back; } #if !ALWAYS_PERMIT /** Construct the request. The request received from stdin was split for sanity check, now rebuild the request before sending it. @param pj Pointer to prqd job structure. @return 1 on success, 0 on error. */ static int construct_request DK_P1(PJ *,pj) { int back = 0; prqd_pj_get_names(pj); if((pj->uname) && (pj->pname)) { back = 1; if(pj->tname) { if(pj->pages) { if(prqd_pj_get_arg(pj, 'D')) { sprintf( pj->b2, "%s '-n%s' '-P%s' '-p%s' '-J%s' '-h%s' '-D%s'", pj->rqtstr, pj->uname, pj->pname, pj->pages, pj->tname, XSTR(short_host_name(pj->hname)), prqd_pj_get_arg(pj, 'D') ); } else { sprintf( pj->b2, "%s '-n%s' '-P%s' '-p%s' '-J%s' '-h%s'", pj->rqtstr, pj->uname, pj->pname, pj->pages, pj->tname, XSTR(short_host_name(pj->hname)) ); } } else { if(prqd_pj_get_arg(pj, 'D')) { sprintf( pj->b2, "%s '-n%s' '-P%s' '-J%s' '-h%s' '-D%s'", pj->rqtstr, pj->uname, pj->pname, pj->tname, XSTR(short_host_name(pj->hname)), prqd_pj_get_arg(pj, 'D') ); } else { sprintf( pj->b2, "%s '-n%s' '-P%s' '-J%s' '-h%s'", pj->rqtstr, pj->uname, pj->pname, pj->tname, XSTR(short_host_name(pj->hname)) ); } } pj->rqtstr = NULL; strcpy(pj->b1, pj->b2); (pj->b2)[0] = '\0'; } else { if(pj->pages) { if(prqd_pj_get_arg(pj, 'D')) { sprintf( pj->b2, "%s '-n%s' '-P%s' '-p%s' '-JUNNAMED' '-h%s' '-D%s'", pj->rqtstr, pj->uname, pj->pname, pj->pages, XSTR(short_host_name(pj->hname)), prqd_pj_get_arg(pj, 'D') ); } else { sprintf( pj->b2, "%s '-n%s' '-P%s' '-p%s' '-JUNNAMED' '-h%s'", pj->rqtstr, pj->uname, pj->pname, pj->pages, XSTR(short_host_name(pj->hname)) ); } } else { if(prqd_pj_get_arg(pj, 'D')) { sprintf( pj->b2, "%s '-n%s' '-P%s' '-JUNNAMED' '-h%s' '-D%s'", pj->rqtstr, pj->uname, pj->pname, XSTR(short_host_name(pj->hname)), prqd_pj_get_arg(pj, 'D') ); } else { sprintf( pj->b2, "%s '-n%s' '-P%s' '-JUNNAMED' '-h%s'", pj->rqtstr, pj->uname, pj->pname, XSTR(short_host_name(pj->hname)) ); } } pj->rqtstr = NULL; strcpy(pj->b1, pj->b2); (pj->b2)[0] = '\0'; } } if(!back) { prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(64)); } return back; } #endif /** Pass request to prqd. Use the UNIX domain socket to connect to prqd, send request, wait for response and print response if necessary. @param pj Pointer to prqd job structure. @return 1 on success, 0 on error. */ static int run_request DK_P1(PJ *,pj) { int back = 0; int sock = -1; int cc = 0; struct sockaddr_un soun; size_t sz, sz2; int is_first; if((pj->prqdc)->sockname) { if(strlen((pj->prqdc)->sockname) < 108) { sock = socket(PF_UNIX, SOCK_STREAM, 0); if(sock > -1) { soun.sun_family = AF_UNIX; strcpy(soun.sun_path, (pj->prqdc)->sockname); if(connect(sock, (struct sockaddr*)(&soun), SZSOUN) == 0) { syslog(LOG_INFO, "Successfully connected to server."); sz = 1 + strlen(pj->b1); sz2 = send(sock, pj->b1, sz, 0); if(sz2 >= sz) { shutdown(sock, SHUT_WR); cc = 1; is_first = 1; while(cc) { sz2 = recv(sock, pj->b1, pj->sz_b1, 0); if(sz2 >= pj->sz_b1) { (pj->b1)[pj->sz_b1 - 1] = '\0'; } else { (pj->b1)[sz2] = '\0'; } if(sz2 > 0) { if(is_first) { is_first = 0; if(pj->must_respond) { strcpy(pj->b2, pj->b1); } } back = 1; } else { cc = 0; } } if(!(pj->must_respond)) { back = 1; } } else { prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(66)); } } else { prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(65)); } close(sock); sock = -1; } else { prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(4)); } } else { prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(5), (pj->prqdc)->sockname); } } else { prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(24)); } return back; } /** Process one request. Before passing the request to prqd do sanity checks. When running as prqdinfo allow info requests only. For prqdcl allow jobstart, filestart, fileend and jobend requests. For prqdadm allow the full protocol (all of the above and control requests). @param pj Pointer to prqd job structure. */ static void process_request DK_P1(PJ *,pj) { char *p1, *p2, **ptr; int i = 0; #if DEBUG { time_t timer; struct tm *tm; FILE *f; f = dksf_fopen("/tmp/prqdcl.log", "a"); if(f) { time(&timer); tm = localtime(&timer); fprintf(f, "PID=%lu ", (unsigned long)getpid()); if(tm) { fprintf(f, "TIME=%04d/%02d/%02d-%02d:%02d:%02d", (1900 + tm->tm_year), (1 + tm->tm_mon), tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec ); } fputc('\n', f); fputs(pj->b1, f); fflush(f); fclose(f); } } #endif ptr = pj->args; for(i = 0; i < 52; i++) { *(ptr++) = NULL; } pj->rqtstr = p1 = dkstr_start(pj->b1, NULL); if(p1) { dkstr_chomp(p1, NULL); p2 = dkstr_next(p1, NULL); prqd_pj_get_rq_type(pj, p1); switch(pj->rqt) { #if !PRQDINFO case PRQD_RQ_T_JOBSTART: { prqd_pj_set_all_args(pj, p2); #if ALWAYS_PERMIT strcpy(pj->b2, "ACCEPT\n"); pj->must_respond = 0x01; #else /* if ALWAYS_PERMIT */ pj->must_respond = 0x01; if(construct_request(pj)) { if(!run_request(pj)) { prqd_pj_use_deny_action(pj); } } else { prqd_pj_use_deny_action(pj); } #endif /* if ALWAYS_PERMIT */ } break; case PRQD_RQ_T_FILESTART: case PRQD_RQ_T_START: { pj->must_respond = 0x00; prqd_pj_set_all_args(pj, p2); if(construct_request(pj)) { (void)run_request(pj); } } break; case PRQD_RQ_T_END: case PRQD_RQ_T_FILEEND: { pj->must_respond = 0x00; prqd_pj_set_all_args(pj, p2); if(construct_request(pj)) { (void)run_request(pj); } } break; case PRQD_RQ_T_JOBEND: { pj->must_respond = 0x00; prqd_pj_set_all_args(pj, p2); if(construct_request(pj)) { (void)run_request(pj); } } break; #if PRQDADM case PRQD_RQ_T_CONTROL: { pj->must_respond = 0x00; if(p2) { sprintf(pj->b2, "control %s", p2); strcpy(pj->b1, pj->b2); (void)run_request(pj); } } break; #else /* if PRQDADM */ case PRQD_RQ_T_CONTROL: { pj->must_respond = 0x00; } break; #endif /* if PRQDADM */ #endif /* if !PRQDINFO */ case PRQD_RQ_T_INFO: { pj->must_respond = 0x01; if(p2) { sprintf(pj->b2, "info %s", p2); strcpy(pj->b1, pj->b2); (void)run_request(pj); } sleep(1); } break; default: { prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(63), p1); } break; } } } /** Check whether to continue. @param pj Pointer to prqd job structure. @return 1 for ``can continue'', 0 for need to finish. */ static int prqd_cc1 DK_P1(PJ *,pj) { int back = 1; if(pj->e1) back = 0; return back; } /** Run a service session. Read input from standard input as long as possible and process the input requests. If the program is run as prqdcl, process only one request and exit. */ static void run DK_P1(PJ *,pj) { int cc; char *p1; pj->prqdc = prqdconf_new_prqdc(pj, prqdconf_get_cfgfile()); if(pj->prqdc) { cc = 1; pj->e1 = pj->e2 = pj->e3 = 0; /* For info and adm handle multiple input lines in one session. */ #if PRQDINFO || PRQDADM while((cc) && prqd_cc1(pj)) { pj->must_respond = 0x00; (pj->b2)[0] = '\0'; if(fgets(pj->b1, pj->sz_b1, stdin)) { p1 = dkstr_start(pj->b1, NULL); if(p1) { dkstr_chomp(p1, NULL); process_request(pj); if(pj->must_respond) { fputs(pj->b2, stdout); fflush(stdout); } } } else { cc = 0; } } /* LPRng invokes prqdcl once at start of job and once at end of job, it waits for the return code. So prqdcl has to process one input line and exit. */ #else /* if PRQDINFO || PRQDADM */ pj->must_respond = 0x00; (pj->b2)[0] = '\0'; if(fgets(pj->b1, pj->sz_b1, stdin)) { p1 = dkstr_start(pj->b1, NULL); if(p1) { dkstr_chomp(p1, NULL); process_request(pj); if(pj->must_respond) { fputs(pj->b2, stdout); fflush(stdout); } } } #endif /* if PRQDINFO || PRQDADM */ prqdconf_delete_prqdc(pj->prqdc); pj->prqdc = NULL; } else { prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(22)); } } /** Buffer to read input line. */ static char inbuffer[PRQD_BUFFER_SIZE]; /** Buffer to construct output line. */ static char outbuffer[PRQD_BUFFER_SIZE]; /** Buffer for database key. */ static char dbkey[PRQD_DBENTRY_SIZE]; /** Buffer for database value. */ static char dbval[PRQD_DBENTRY_SIZE]; /** Buffer for program name. */ static char pgnamebuffer[PRQD_DBENTRY_SIZE]; /** Array for up to 53 request arguments. */ static char *args[53]; /** The main function. Initialize a prqd job structure and invoke run() for the real work. @param argc Number of command line arguments. @param argv Command line arguments array. @return 0 on success, positive numbers on error. */ #if DK_HAVE_PROTOTYPES int main(int argc, char *argv[]) #else int main(argc, argv) int argc; char *argv[]; #endif { PJ pj; #line 493 "prqdcl.ctr" 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); openlog("prqdcl", 0, LOG_LPR); run(&pj); closelog(); #line 506 "prqdcl.ctr" exit(0); return 0; }