%% options copyright owner = Dirk Krause copyright year = 2012-2014 license = bsd %% header #include "dk3types.h" #include "dk3sock.h" #include "printqds.h" /** Limit for one user or group. */ typedef struct { char const *name; /**< User or group name. */ unsigned long limit; /**< Page limit. */ } pqd_limit_t; /** Printer class. Limits are valid for all printers in a class in summary. */ typedef struct { char const *name; /**< Class name. */ dk3_sto_t *s_u; /**< User limits storage. */ dk3_sto_it_t *i_u; /**< Iterator through user limits storage. */ dk3_sto_t *s_g; /**< Group limits storage. */ dk3_sto_it_t *i_g; /**< Iterator for group limits storage. */ unsigned long dl; /**< Default limit. */ int da; /**< Deny action: 1=hold, 0=remove. */ } pqd_class_t; /** Printer. */ typedef struct { char const *name; /**< Printer name. */ pqd_class_t *cl; /**< Printer class. */ } pqd_printer_t; /** Printer alias. */ typedef struct { char const *name; /**< Alias name. */ pqd_printer_t *pr; /**< The real printer. */ } pqd_alias_t; /** Printqd configuration data. */ typedef struct { char const *usn; /**< UNIX domain socket file name. */ char const *dbn; /**< Database name. */ dk3_sto_t *s_c; /**< Storage for classes. */ dk3_sto_it_t *i_c; /**< Iterator for class storage. */ dk3_sto_t *s_p; /**< Storage for printers. */ dk3_sto_it_t *i_p; /**< Iterator for printers. */ dk3_sto_t *s_a; /**< Storage for aliases. */ dk3_sto_it_t *i_a; /**< Iterator for aliases storage. */ dk3_sto_t *s_i; /**< Storage for allowed info clients. */ dk3_sto_it_t *i_i; /**< Iterator for info clients storage. */ uid_t uid; /**< Run as user. */ gid_t gid; /**< Run as group. */ int ubl; /**< UNIX domain socket backlog. */ int ibl; /**< Info socket backlog. */ unsigned short port; /**< Port number for info requests. */ } pqd_conf_t; /** Printqd job data. */ typedef struct { pqd_conf_t cfg; /**< Configuration data. */ dk3_app_t *app; /**< Application structure. */ char const * const *lmsg; /**< Localized messages. */ char const * const *cmsg; /**< Localized messages for configuartion. */ char const *tmpn; /**< File name for temporary file. */ dk3_dbi_t *db; /**< Database. */ char *p_i; /**< Buffer for input data. */ char *p_o; /**< Buffer for output data. */ size_t sz_i; /**< Size of input data buffer. */ size_t sz_o; /**< Size of output data buffer. */ dk3_socket_t lso; /**< Listener socket. */ int frun; /**< Flag: First run. */ int fres; /**< Flag: Send response. */ int rqt; /**< Request type. */ } pqd_job_t; #ifdef __cplusplus extern "C" { #endif /* MODULE printqd ============== */ /* MODULE pqdconf ============== */ /** Obtain localized error messages related to configuration. @param app Application structure. @return Pointer to localized (success) or non-localized (error) array. */ dkChar const * const * pqdconf_get_messages(dk3_app_t *app); /** Read a configuration. @param cfg Configuration buffer. @param fn File name. @param type Program type, PQD_PROGRAM_TYPE_xxx. @param app Application structure for diagnostics. @param cmsg Localized message texts for configuration reading. @return 1 on success, 0 on error. */ int pqdconf_read( pqd_conf_t *cfg, char const *fn, int type, dk3_app_t *app, dkChar const * const *cmsg ); /** Clean up configuration, release dynamically allocated data. @param cfg Configuration to clean up. */ void pqdconf_cleanup(pqd_conf_t *cfg); /** Check configuration for completeness. @param cfg Configuration to check. @param type Program type, PQD_PROGRAM_TYPE_xxx. @param app Application structure for diagnostics. @param cmsg Localized messages for configuration file reading. @return 1 on success, 0 on error. */ int pqdconf_check( pqd_conf_t *cfg, int type, dk3_app_t *app, dkChar const * const *cmsg ); /* MODULE pqdproto =============== */ /** Process one request stored in job->p_i. If it is necessary to send a response, set the job->fres flag and place the response as 0x00-terminated string in job->p_o. @param job Job structure. */ void pqdproto_process(pqd_job_t *job); /* MODULE pqdstr ============= */ /** Get localized messages. @param app Application structure. @return Pointer to localized (success) or non-localized (error) array. */ dkChar const * const * pqdstr_get_messages(dk3_app_t *app); /* MODULE pqdtool ============== */ /** Initially reset job data. @param job Job structure. */ void pqdtool_job_init_first(pqd_job_t *job); /** Finally clean up and release all resources. @param job Job structure. */ void pqdtool_job_cleanup_last(pqd_job_t *job); /** Create directory for file name. @param fn File name for which to create a directory for. @param mo Mode for directory and parents. @param uid User ID of owner. @param gid Group ID of owner. @param job Job structure. @return 1 on success, 0 on error. */ int pqdtool_mkdir_for( char const *fn, mode_t mo, uid_t uid, gid_t gid, pqd_job_t *job ); #ifdef __cplusplus } #endif /** Program type: The daemon itself (printqd). */ #define PQD_PROGRAM_TYPE_DAEMON 0 /** Program type: Data multiplexor (printqdc). */ #define PQD_PROGRAM_TYPE_MULTIPLEXOR 1 /** Program type: Administrator client (printqda). */ #define PQD_PROGRAM_TYPE_ADMIN 2 /** Program type: Information daemon (printqdi). */ #define PQD_PROGRAM_TYPE_INFO 3 /** Handle job: Accept job for printing. */ #define PRINTQD_RESPONSE_ACCEPT 0 /** Handle job: Remove job because printing is denied. */ #define PRINTQD_RESPONSE_REMOVE 1 /** Handle job: Hold job for later decision. */ #define PRINTQD_RESPONSE_HOLD 2 #ifndef PRINTQD_DEFAULT_PORT /** Default port number to use if not configured in configuration file. */ #define PRINTQD_DEFAULT_PORT 9101 #endif #ifndef PQD_CONFFILE /** Configuration file name. */ #define PQD_CONFFILE "/printqd/printqd.conf" #endif %% module /** @file printqd.c The printqd daemon. The printqd accounting and quota system consists of the following programs: printqd is a standing daemon listening on a UNIX domain socket for data and administrative connection and on a TCP port for info connections. LPRng does not directly contact this daemon, it uses the printqdm program (printqd multiplexor) to communicate to printqd. printqdc is the printqd multiplexor. LPRng and/or the filters run by LPRng run this program as a pipe. The program receives accounting data in stdin, pre-processes data, connects to printqd, sends the pre-processed data, waits for a response and writes the response - if any - to stdout. printqda is the printqdm administration program. Printqd processes exactly one request on a connected socket. Printqdc and printqda read input line by line and establish a new connection for each new input line. */ #include "printqd.h" #include #if DK3_HAVE_STDLIB_H #include #endif #if DK3_HAVE_UNISTD_H #include #endif #if DK3_HAVE_PROCESS_H #include #endif #if DK3_HAVE_SIGNAL_H #include #endif $!trace-include #if DK3_CHAR_SIZE == 1 #if DK3_HAVE_STRUCT_SOCKADDR_UN #if (DK3_HAVE_SIGACTION) || (DK3_HAVE_SIGSET) #if DK3_HAVE_GETPWNAM && DK3_HAVE_GETGRNAM /* Start of normal code. */ /** Application group name. */ static char const pqd_groupname[] = { "dkt-3" }; /** Configuration file name relative to sysconf directory. */ static char const pqd_conffile[] = { PQD_CONFFILE }; /** Current directory when starting the program. */ static char pqd_cwd[DK3_MAX_PATH]; /** Full name of configuration file in use. */ static char pqd_config_file[DK3_MAX_PATH]; /** Input buffer. */ static char pqd_input_buffer[PQD_INPUT_BUFFER_SIZE]; /** Output buffer. */ static char pqd_output_buffer[PQD_OUTPUT_BUFFER_SIZE]; /** Exit status code. */ static int exval = 1; /** Keyword (not localized) used by the module. */ static char const * const pqd_c8_kw[] = { $!string-table # # 0 Root directory. # / # # 1 Name "printqd" # printqd $!end }; /** Read volatile variable via pointer. @param vp Pointer to volatile variable. @return Variable contents. */ static dk3_sig_atomic_t pqd_read_vol_var(DK3_VOLATILE dk3_sig_atomic_t *vp) { return *vp; } /** Pass through pointer to volatile variable. @param vp Pointer to volatile variable. @return Pointer vp. */ static DK3_VOLATILE dk3_sig_atomic_t * pqd_pass_vol_ptr(DK3_VOLATILE dk3_sig_atomic_t *vp) { return vp; } /* SIGNAL HANDLING VARIABLES AND FUNCTIONS */ /** Flag: SIGHUP signal was found. */ static DK3_VOLATILE dk3_sig_atomic_t pqd_sighup_received = 0; /** Handler for signal SIGHUP. @param signo Signal number. */ static void pqd_sighup_handler(int signo) { #if 0 pqd_sighup_received = 1; #else *pqd_pass_vol_ptr(&pqd_sighup_received) = 1; #endif } /** Flag: SIGINT signal was found. */ static DK3_VOLATILE dk3_sig_atomic_t pqd_sigint_received = 0; /** Handler for signal SIGINT. @param signo Signal number. */ static void pqd_sigint_handler(int signo) { #if 0 pqd_sigint_received = 1; #else *pqd_pass_vol_ptr(&pqd_sigint_received) = 1; #endif } /** Flag: SIGTERM signal was found. */ static DK3_VOLATILE dk3_sig_atomic_t pqd_sigterm_received = 0; /** Handler for signal SIGTERM. @param signo Signal number. */ static void pqd_sigterm_handler(int signo) { #if 0 pqd_sigterm_received = 1; #else *pqd_pass_vol_ptr(&pqd_sigterm_received) = 1; #endif } /** Flag: SIGPIPE signal was found. */ static DK3_VOLATILE dk3_sig_atomic_t pqd_sigpipe_received = 0; /** Handler for signal SIGPIPE. @param signo Signal number. */ static void pqd_sigpipe_handler(int signo) { #if 0 pqd_sigpipe_received = 1; #else *pqd_pass_vol_ptr(&pqd_sigpipe_received) = 1; #endif } /* SERVICE FUNCTIONS */ /** Flag: Can continue in outer loop. */ static int pqd_icc_outer = 1; /** Flag: Can continue in inner loop. */ static int pqd_icc_inner = 1; /** Check whether or not we can continue in the outer loop. @return 1 for can continue, 0 for exit. */ int pqd_cc_outer(void) { int back = 1; if(pqd_read_vol_var(&pqd_sigint_received)) { back = 0; } else { if(pqd_read_vol_var(&pqd_sigterm_received)) { back = 0; } else { if(!(pqd_icc_outer)) { back = 0; } } } $? "= pqd_cc_outer %d", back return back; } /** Check whether or not we can continue in inner loop. @return 1 for can continue, 0 for exit. */ int pqd_cc_inner(void) { int back; back = pqd_cc_outer(); if(back) { if(pqd_read_vol_var(&pqd_sighup_received)) { back = 0; } else { if(!(pqd_icc_inner)) { back = 0; } } } $? "= pqd_cc_inner %d", back return back; } /** Initialize job structure once before running the service. @param job Job structure. @return 1 on success, 0 on error. */ static int pqd_run_init(pqd_job_t *job) { char bu[DK3_MAX_PATH]; int back = 0; $? "+ pqd_run_init" job->frun = 1; if(dk3app_get_temp_file_name(job->app, bu, sizeof(bu))) { job->tmpn = dk3str_c8_dup_app(bu, job->app); if(job->tmpn) { back = 1; } else { pqd_icc_outer = 0; /* Failed to create file name copy. */ #if DK3_HAVE_SYSLOG openlog(pqd_c8_kw[1], LOG_PID, LOG_LPR); syslog(LOG_ERR, "Not enough memory!"); closelog(); #endif } } else { pqd_icc_outer = 0; /* Failed to obtain file name for temporary file! */ #if DK3_HAVE_SYSLOG openlog(pqd_c8_kw[1], LOG_PID, LOG_LPR); syslog(LOG_ERR, "Failed to obtain file name for temporary file!"); closelog(); #endif } $? "- pqd_run_init %d", back return back; } /** Clean up job structure when done. @param job Job structure. */ static void pqd_run_end(pqd_job_t *job) { $? "+ pqd_run_end" dk3_release(job->tmpn); $? "- pqd_run_end" } /** Initialize at start of outer loop. @param job Job structure. @return 1 on success, 0 on error (abort program). */ static int pqd_loop_init(pqd_job_t *job) { int back = 0; $? "+ pqd_loop_init" back = pqdconf_read( &(job->cfg), pqd_config_file, PQD_PROGRAM_TYPE_DAEMON, job->app, job->cmsg ); if(back) { $? ". configuration was read successfully" back = pqdconf_check( &(job->cfg), PQD_PROGRAM_TYPE_DAEMON, job->app, job->cmsg ); if(!(back)) { #if DK3_HAVE_SYSLOG openlog(pqd_c8_kw[1], LOG_PID, LOG_LPR); syslog(LOG_ERR, "Incomplete configuration!"); closelog(); #endif } } else { $? "! failed to read configuration" #if DK3_HAVE_SYSLOG openlog(pqd_c8_kw[1], LOG_PID, LOG_LPR); syslog(LOG_ERR, "Error while reading configuration file!"); closelog(); #endif } $? "- pqd_loop_init %d", back return back; } /** Clean up at end of outer loop. @param job Job structure. */ static void pqd_loop_end(pqd_job_t *job) { $? "+ pqd_loop_end" pqdconf_cleanup(&(job->cfg)); $? "- pqd_loop_end" } /** Before changing to non-root user and group we must ensure all our files exits and are owned by the correct user and group. @param job Job structure. */ static void pqd_create_files_and_change_user(pqd_job_t *job) { char bu[DK3_MAX_PATH]; /* File name buffer. */ dk3_dbi_t *db; /* Database opened for test. */ char const *p1; /* Next separator. */ char const *p2; /* Start of database name. */ int res; /* Result from mkdir. */ $? "+ pqd_create_files_and_change_user" /* Create socket parent directory. */ if((job->cfg).usn) { res = pqdtool_mkdir_for( (job->cfg).usn, 0750, (job->cfg).uid, (job->cfg).gid, job ); if(!(res)) { pqd_icc_outer = 0; #if DK3_HAVE_SYSLOG openlog(pqd_c8_kw[1], LOG_PID, LOG_LPR); syslog(LOG_ERR, "Failed to create parent directory for UNIX socket!"); closelog(); #endif } } /* Create parent directory for temporary file. */ if(job->tmpn) { res = pqdtool_mkdir_for( job->tmpn, 0750, (job->cfg).uid, (job->cfg).gid, job ); if(!(res)) { pqd_icc_outer = 0; #if DK3_HAVE_SYSLOG openlog(pqd_c8_kw[1], LOG_PID, LOG_LPR); syslog(LOG_ERR, "Failed to create parent directory for temp file!"); closelog(); #endif } } /* Create database parent directory. */ if((job->cfg).dbn) { p2 = (job->cfg).dbn; p1 = dk3str_c8_chr((job->cfg).dbn, ':'); if(p1) { if(p1[1] == ':') { p2 = &(p1[2]); } } if(strlen(p2) < sizeof(bu)) { strcpy(bu, p2); res = pqdtool_mkdir_for( bu, 0750, (job->cfg).uid, (job->cfg).gid, job ); if(!(res)) { pqd_icc_outer = 0; #if DK3_HAVE_SYSLOG openlog(pqd_c8_kw[1], LOG_PID, LOG_LPR); syslog(LOG_ERR, "Failed to create parent directory for database!"); closelog(); #endif } } else { pqd_icc_outer = 0; /* ERROR: Database name too long! */ dk3app_log_3(job->app, DK3_LL_ERROR, job->lmsg, 8, 9, (job->cfg).dbn); #if DK3_HAVE_SYSLOG openlog(pqd_c8_kw[1], LOG_PID, LOG_LPR); syslog(LOG_ERR, "Database file name too long!"); closelog(); #endif } if(pqd_icc_outer) { db = dk3dbi_open_app( (job->cfg).dbn, DK3_DB_TYPE_UNKNOWN, (DK3_DB_ACCESS_READ | DK3_DB_ACCESS_WRITE), NULL, job->app ); if(db) { dk3dbi_set_c8_string(db, "o:test", "test"); dk3dbi_sync(db); dk3dbi_change_user_and_permissions( db, (job->cfg).uid, (job->cfg).gid, 0660 ); dk3dbi_close(db); } else { } } } /* Switch to other GID and UID (GID switch first). */ if(((job->cfg).uid) || ((job->cfg).gid)) { if(!dk3app_change_user(job->app, (job->cfg).uid, (job->cfg).gid)) { pqd_icc_outer = 0; #if DK3_HAVE_SYSLOG openlog(pqd_c8_kw[1], LOG_PID, LOG_LPR); syslog(LOG_ERR, "Failed to re-own application data!"); closelog(); #endif } if((job->cfg).gid) { if(0 != setgid((job->cfg).gid)) { pqd_icc_outer = 0; /* ERROR: Failed to switch group! */ dk3app_log_1(job->app, DK3_LL_ERROR, job->lmsg, 10); #if DK3_HAVE_SYSLOG openlog(pqd_c8_kw[1], LOG_PID, LOG_LPR); syslog(LOG_ERR, "Failed to switch group!"); closelog(); #endif } } if((job->cfg).uid) { if(0 != setuid((job->cfg).uid)) { pqd_icc_outer = 0; /* ERROR: Failed to switch user! */ dk3app_log_1(job->app, DK3_LL_ERROR, job->lmsg, 11); #if DK3_HAVE_SYSLOG openlog(pqd_c8_kw[1], LOG_PID, LOG_LPR); syslog(LOG_ERR, "Failed to switch user!"); closelog(); #endif } } } $? "- pqd_create_files_and_change_user" } /** Open listener socket and database. @param job Job structure. @return 1 on success, 0 on error. */ static int pqd_passes_init(pqd_job_t *job) { int back = 0; mode_t oldumask; $? "+ pqd_passes_init" oldumask = umask(0); #if (!(VERSION_BEFORE_20140416)) /* 2014-04-16 Remove existing UNIX domain socket before creating the new socket. */ (void)dk3sf_c8_remove_file_app((job->cfg).usn, NULL); #endif job->lso = dk3socket_un_listener((job->cfg).usn,(job->cfg).ubl,NULL,job->app); if(INVALID_SOCKET != job->lso) { chmod((job->cfg).usn, 0660); umask(oldumask); job->db = dk3dbi_open_app( (job->cfg).dbn, DK3_DB_TYPE_UNKNOWN, (DK3_DB_ACCESS_READ | DK3_DB_ACCESS_WRITE), NULL, job->app ); if(job->db) { dk3dbi_set_c8_string(job->db, "o:test", "test"); dk3dbi_sync(job->db); back = 1; } } else { umask(oldumask); pqd_icc_outer = 0; /* ERROR: Failed to listen on socket! */ #if DK3_HAVE_SYSLOG openlog(pqd_c8_kw[1], LOG_PID, LOG_LPR); syslog(LOG_ERR, "Failed to listen on UNIX domain socket!"); closelog(); #endif } $? "- pqd_passes_init %d", back return back; } /** Close database and listener socket. @param job Job structure. */ static void pqd_passes_end(pqd_job_t *job) { $? "+ pqd_passes_end" if(job->db) { dk3dbi_close(job->db); } job->db = NULL; if(INVALID_SOCKET != job->lso) { (void)dk3socket_close(job->lso, NULL, job->app); } job->lso = INVALID_SOCKET; dk3sf_c8_remove_file_app((job->cfg).usn, job->app); $? "- pqd_passes_end" } /** Run one service pass (listen for connection, accept connection, receive request, send response, and close connection. @param job Job structure. */ static void pqd_pass(pqd_job_t *job) { dk3_socket_t ss; /* Session socket. */ int rb; /* Number of bytes read. */ int wb; /* Bytes written. */ int ok; /* Flag: No error so far. */ $? "+ pqd_pass" if(pqd_cc_inner()) { ss = dk3socket_accept(job->lso, NULL, NULL, NULL, job->app); if(INVALID_SOCKET != ss) { /* Read request and process if data was read. */ rb = dk3socket_recv(ss,job->p_i,(job->sz_i - 1),0L,0L,NULL,job->app); if((rb > 0) && (pqd_cc_inner())) { /* Finalize string. */ (job->p_i)[(rb<(int)(job->sz_i)) ? (size_t)rb : ((job->sz_i)-1)] = '\0'; ok = 1; dk3str_c8_delnl(job->p_i); /* Process request. */ job->fres = 0; job->rqt = -1; (job->p_o)[0] = '\0'; pqdproto_process(job); /* Send response if required. */ if(job->fres) { rb = (int)strlen(job->p_o); wb = dk3socket_send(ss, job->p_o, rb, 0L, 0L, NULL, job->app); if(wb < rb) { ok = 0; } } /* Orderly release for socket if no error yet. */ if((ok) && (pqd_cc_inner())) { if(dk3socket_shutdown(ss, DK3_TCPIP_SHUTDOWN_WRITE, NULL, job->app)) { while((ok) && (pqd_cc_inner())) { ok = 0; rb = dk3socket_recv( ss, job->p_i, (job->sz_i - 1), 0L, 0L, NULL, NULL ); if(rb > 0) { ok = 1; } } } } /* Sync database. */ if(pqd_cc_inner()) { switch(job->rqt) { case 2: case 3: case 4: { $? ". sync db" dk3dbi_sync(job->db); } break; } } } else { $? "! recv" } dk3socket_close(ss, NULL, NULL); } else { $? "! accept" } } $? "- pqd_pass" } /** Run the service. @param job Job structure. */ static void pqd_service(pqd_job_t *job) { $? "+ pqd_service" if(pqd_run_init(job)) { while(pqd_cc_outer()) { #if 0 pqd_sighup_received = 0; #else *pqd_pass_vol_ptr(&pqd_sighup_received) = 0; #endif if(pqd_loop_init(job)) { if(job->frun) { job->frun = 0; pqd_create_files_and_change_user(job); } if(pqd_cc_inner()) { if(pqd_passes_init(job)) { while(pqd_cc_inner()) { pqd_pass(job); } } pqd_passes_end(job); } } else { pqd_icc_outer = 0; /* Failed to initialize loop! */ } pqd_loop_end(job); } #if DK3_HAVE_SYSLOG /* SYSLOG: Service going down! */ openlog(pqd_c8_kw[1], LOG_PID, LOG_LPR); if(pqd_icc_outer) { syslog(LOG_NOTICE, "Exiting printqd normally."); } else { syslog( LOG_CRIT, "Exiting printqd due to error!" ); } closelog(); #endif if(pqd_icc_outer) { dk3app_log_1(job->app, DK3_LL_INFO, job->lmsg, 18); } else { dk3app_log_1(job->app, DK3_LL_ERROR, job->lmsg, 19); } } pqd_run_end(job); $? "- pqd_service" } /** Construct configuration file name (absolute path) in pqd_config_file. If there are command line arguments, the file name is constructed from the original current working directory and the first command line argument. Otherwise a default file name is constructed from the system configuratin directory and the pqd_conffile value. @param job Job structure. @return 1 on success, 0 on error. */ static int pqd_construct_config_file_name(pqd_job_t *job) { int back = 0; dkChar const * const *xargv; /* Command line arguments array. */ dkChar const *scd; /* System configuration directory. */ int xargc; /* Number of command line arguments. */ int res; /* Result from path appending. */ $? "+ pqd_construct_config_file_name" pqd_config_file[0] = '\0'; xargc = dk3app_get_argc(job->app); xargv = dk3app_get_argv(job->app); if(xargc > 1) { strcpy(pqd_config_file, pqd_cwd); res = dk3str_c8_append_path_app( pqd_config_file, sizeof(pqd_config_file), xargv[1], job->app ); if(res) { back = 1; } } else { scd = dk3inst_get_directory(1); if((strlen(scd) + strlen(pqd_conffile)) < sizeof(pqd_config_file)) { strcpy(pqd_config_file, scd); strcat(pqd_config_file, pqd_conffile); back = 1; } else { /* ERROR: Strings too long! */ dk3app_log_3(job->app, DK3_LL_ERROR, job->lmsg, 12, 13, scd); } } $? "- pqd_construct_config_file_name %d \"%s\"", back, pqd_config_file return back; } /** Run the main part of the program. @param job Job structure. */ static void pqd_run(pqd_job_t *job) { #if DK3_HAVE_SIGACTION struct sigaction sanewhup; /* Install new handler for HUP */ struct sigaction sanewint; /* Install new handler for INT */ struct sigaction sanewterm; /* Install new handler for TERM */ struct sigaction sanewpipe; /* Install new handler for PIPE */ struct sigaction sabckhup; /* Backup handler for HUP */ struct sigaction sabckint; /* Backup handler for INT */ struct sigaction sabckterm; /* Backup handler for TERM */ struct sigaction sabckpipe; /* Backup handler for PIPE */ int sigok = 0; /* Flag: Signal handling setup ok */ $? "+ pqd_run" pqd_icc_outer = 1; pqd_icc_inner = 1; sanewhup.sa_handler = pqd_sighup_handler; sanewint.sa_handler = pqd_sigint_handler; sanewterm.sa_handler = pqd_sigterm_handler; sanewpipe.sa_handler = pqd_sigpipe_handler; sanewhup.sa_flags = 0; sanewint.sa_flags = 0; sanewterm.sa_flags = 0; sanewpipe.sa_flags = 0; if (sigemptyset(&(sanewhup.sa_mask)) == 0) { if (sigaddset(&(sanewhup.sa_mask), SIGHUP) == 0) { if (sigaction(SIGHUP, &sanewhup, &sabckhup) == 0) { if (sigemptyset(&(sanewint.sa_mask)) == 0) { if (sigaddset(&(sanewint.sa_mask), SIGINT) == 0) { if (sigaction(SIGINT, &sanewint, &sabckint) == 0) { if (sigemptyset(&(sanewterm.sa_mask)) == 0) { if (sigaddset(&(sanewterm.sa_mask), SIGTERM) == 0) { if (sigaction(SIGTERM, &sanewterm, &sabckterm) == 0) { if (sigemptyset(&(sanewpipe.sa_mask)) == 0) { if (sigaddset(&(sanewpipe.sa_mask), SIGPIPE) == 0) { if (sigaction(SIGPIPE, &sanewpipe, &sabckpipe) == 0) { sigok = 1; pqd_icc_outer = 1; pqd_icc_inner = 1; if(pqd_construct_config_file_name(job)) { pqd_service(job); } (void)sigaction(SIGPIPE, &sabckpipe, NULL); } } } (void)sigaction(SIGTERM, &sabckterm, NULL); } } } (void)sigaction(SIGINT, &sabckint, NULL); } } } (void)sigaction(SIGHUP, &sabckhup, NULL); } } } if (0 == sigok) { /* ERROR: Signal handling setup failed */ dk3app_log_1(job->app, DK3_LL_ERROR, job->lmsg, 20); #if DK3_HAVE_SYSLOG openlog(pqd_c8_kw[1], LOG_PID, LOG_LPR); syslog(LOG_ERR, "Signal handling setup failed!"); closelog(); #endif } $? "- pqd_run" #else dk3_signal_disp_t disp_hup; /* Original disposition for SIGHUP. */ dk3_signal_disp_t disp_int; /* Original disposition for SIGINT. */ dk3_signal_disp_t disp_term; /* Original disposition for SIGTERM. */ dk3_signal_disp_t disp_pipe; /* Original disposition for SIGPIPE. */ $? "+ pqd_run" pqd_icc_outer = 1; pqd_icc_inner = 1; disp_hup = sigset(SIGHUP, pqd_sighup_handler); disp_int = sigset(SIGINT, pqd_sigint_handler); disp_term = sigset(SIGTERM, pqd_sigterm_handler); disp_pipe = sigset(SIGPIPE, pqd_sigpipe_handler); pqd_icc_outer = 1; pqd_icc_inner = 1; if(pqd_construct_config_file_name(job)) { pqd_service(job); } sigset(SIGPIPE, disp_pipe); sigset(SIGTERM, disp_term); sigset(SIGINT, disp_int); sigset(SIGHUP, disp_hup); $? "- pqd_run" #endif } /** Program entry point. @param argc @param argv @return 0 on success, any other value indicates an error. */ int main(int argc, char *argv[]) { pqd_job_t job; /* Job structure. */ pid_t pid; /* Process ID from fork(). */ int max; /* Maximum number of files. */ int i; /* Current file to close. */ if(dk3sf_c8_getcwd_app(pqd_cwd, sizeof(pqd_cwd), NULL)) { if(0 == chdir(pqd_c8_kw[0])) { /* All our files are private. */ umask(077); /* Close all files. */ max = dk3sf_get_max_files(); for(i = 0; i < max; i++) { (void)close(i); } /* Run in background. */ pid = fork(); switch(pid) { case 0: { /* Child process. */ #if DK3_HAVE_SETSID setsid(); #else #if DK3_HAVE_SETPGRP setpgrp(); #else #error "No function to disconnect from parent process!" #endif #endif $!trace-init /tmp/printqd.deb $? "+ main" /* Initialize job structure. */ dk3mem_res((void *)(&job), sizeof(pqd_job_t)); pqdtool_job_init_first(&job); /* Set I/O buffers. */ job.p_i = pqd_input_buffer; job.p_o = pqd_output_buffer; job.sz_i = sizeof(pqd_input_buffer); job.sz_o = sizeof(pqd_output_buffer); /* Application structure, localized messages. */ job.app = dk3app_open_daemon( pqd_cwd, argc, (char const * const *)argv, pqd_groupname ); if(job.app) { job.lmsg = pqdstr_get_messages(job.app); job.cmsg = pqdconf_get_messages(job.app); pqd_run(&job); } /* Clean up job structure. */ pqdtool_job_cleanup_last(&job); $? "- main %d", exval $!trace-end } break; case -1: { /* ERROR: Fork failed! */ #if DK3_HAVE_SYSLOG openlog(pqd_c8_kw[1], LOG_PID, LOG_LPR); syslog(LOG_NOTICE, "Failed to create new process using fork()!."); closelog(); #endif } break; default: { /* Parent process. */ exval = 0; } break; } } else { /* ERROR: Failed to change directory! */ fputs( "printqd: ERROR: Failed to change into / direcory!\n", stderr ); fflush(stderr); } } else { /* ERROR: Failed to find current directory! */ fputs( "printqd: ERROR: Failed to find current working directory!\n", stderr ); fflush(stderr); } exit(exval); return exval; } /* End of normal code. */ #else /** Program entry point. @param argc @param argv @return 0 on success, any other value indicates an error. */ int main(int argc, char *argv[]) { fprintf( stderr, "printqd: ERROR: No getpwnam()/getgrnam() functions available!\n" ); fflush(stderr); exit(1); } #endif #else /** Program entry point. @param argc @param argv @return 0 on success, any other value indicates an error. */ int main(int argc, char *argv[]) { fprintf(stderr, "printqd: ERROR: No sigset() function available!\n"); fflush(stderr); exit(1); } #endif #else /** Program entry point. @param argc @param argv @return 0 on success, any other value indicates an error. */ int main(int argc, char *argv[]) { fprintf(stderr, "printqd: ERROR: No UNIX domain sockets available!\n"); fflush(stderr); exit(1); } #endif #else /** Program entry point. @param argc @param argv @return 0 on success, any other value indicates an error. */ int main(int argc, char *argv[]) { fprintf( stderr, "printqd: ERROR: This program does not support wide characters (%d)!\n", DK3_CHAR_SIZE ); fflush(stderr); exit(1); } #endif