%% options copyright owner = Dirk Krause copyright year = 2016-xxxx license = bsd %% header /* CORRECT NEXT LINE AFTER REPLACING PRINTQD */ /** @file printqd.h Definitions for printqd. */ #ifndef DK4CONF_H_INCLUDED #include "dk4conf.h" #endif #ifndef DK4TYPES_H_INCLUDED #include "dk4types.h" #endif #ifndef DK4STO_H_INCLUDED #include "dk4sto.h" #endif #ifndef DK4SOCK_H_INCLUDED #include "dk4sock.h" #endif /** Limit for one user or group. */ typedef struct { char *name; /**< User or group name */ dk4_um_t limit; /**< Page limit. */ } pqd_limit_t; /** Printer class (may contain multiple printers). */ typedef struct { char *name; /**< Class name. */ dk4_sto_t *s_u; /**< User limits storage. */ dk4_sto_it_t *i_u; /**< User limits storage iterator. */ dk4_sto_t *s_g; /**< Group limits storage. */ dk4_sto_it_t *i_g; /**< Group limits storage iterator. */ dk4_um_t dl; /**< Default limit. */ int da; /**< Deny action: 1=hold, 0=remove. */ int hdl; /**< Flag: Have default limit. */ } pqd_class_t; /** Printer. */ typedef struct { char *name; /**< Printer name. */ pqd_class_t *cl; /**< Printer class. */ } pqd_printer_t; /* In service the class is not NULL. If no class is configured for a printer, the program complains and exits. */ /** Printer alias. */ typedef struct { char *name; /**< Alias name. */ pqd_printer_t *pr; /**< The real printer. */ } pqd_printer_alias_t; /** Printqd configuration. */ typedef struct { char *sname; /**< UNIX domain socket file name. */ char *dname; /**< Database name (including type). */ char *lname; /**< Log file name. */ dk4_sto_t *s_c; /**< Storage for classes. */ dk4_sto_it_t *i_c; /**< Iterator for class storage. */ dk4_sto_t *s_p; /**< Storage for printers. */ dk4_sto_it_t *i_p; /**< Iterator for printers storage. */ dk4_sto_t *s_a; /**< Storage for aliases. */ dk4_sto_it_t *i_a; /**< Iterator for aliases storage. */ dk4_sto_t *s_ai; /**< Storage for peers allowed info rq. */ dk4_sto_it_t *i_ai; /**< Iterator for info peers storage. */ dk4_sto_t *s_ad; /**< Storage for peers allowed data rq. */ dk4_sto_it_t *i_ad; /**< Iterator for data peers storage. */ dk4_sto_t *s_aa; /**< Storage for peers allowed admin rq. */ dk4_sto_it_t *i_aa; /**< Iterator for admin peers storage. */ size_t m_loc; /**< Maximum number of local connections. */ size_t m_tcp; /**< Maximum number of TCP connections. */ uid_t uid; /**< Run as specified user. */ uid_t suid; /**< Local socket owner UID. */ gid_t gid; /**< Run as specified group. */ gid_t sgid; /**< Local socket owner GID. */ int slbl; /**< Backlog for local socket. */ int snbl; /**< Backlog for network socket. */ int iglo; /**< Flag: Globally allow info requests. */ int dglo; /**< Flag: Globally allow data requests. */ int aglo; /**< Flag: Globally allow admin requests. */ int linf; /**< Flag: Log info requests. */ int ldb; /**< Flag: Log database modifications. */ unsigned short ptcp; /**< TCP port (info, data, admin), host repr. */ unsigned short pudp; /**< UDP port (info only), host represent. */ } pqd_conf_t; /** Connection record for local socket. */ typedef struct { dk4_socket_t sock; /**< Socket. */ } pqd_l_conn_t; /** Connection record for network socket. */ typedef struct { dk4_sockaddr_storage_t raddr; /**< Remote address. */ dk4_socket_t sock; /**< Socket. */ int pqdpl; /**< Printqd protocol level. */ } pqd_n_conn_t; /** One request to process. */ typedef struct { void *connptr; /**< Connection record for socket. */ dk4_sto_t *sto; /**< Container for conn records. */ struct sockaddr *soa; /**< Remote address, only UDP. */ size_t *pnconn; /**< Addr of variable, NULL for UDP. */ char *cmdptr; /**< Start of command. */ char *argptr; /**< Command arguments. */ size_t szsoa; /**< Size of rem address, only UDP. */ dk4_socket_t sock; /**< Socket to use for response. */ int protlev; /**< Protocol level. */ int action; /**< Action to take. */ int logthis; /**< Flag: Log request and response. */ } pqd_rq_t; /** Data for one user in a class. */ typedef struct { dk4_um_t limit; /**< Limit (maximum page number). */ dk4_um_t used; /**< Number of pages used within limit. */ dk4_um_t account; /**< Remaining pages personal print account. */ int da; /**< Deny action: 0=remove, 1=hold. */ } pqd_account_t; /** Information from one LPRng accounting line. */ typedef struct { char *username; /**< User name. */ char *queuename; /**< Print queue name. */ char *jobtitle; /**< Print job title. */ char *pagecount; /**< Pagecount reported in line. */ } lprng_info_t; /** Printqd protocol levels. */ enum { PQD_PROTO_NONE = 0 , /**< No requests allowed. */ PQD_PROTO_INFO = 1 , /**< Info requests only. */ PQD_PROTO_DATA = 2 , /**< Info and data requests. */ PQD_PROTO_ADMIN = 3 /**< Info, data and admin requests. */ }; /** Commands in the printqd protocol. */ enum { PQD_CMD_NONE = -1 , /**< Illegal command. */ PQD_CMD_JOBSTART = 0 , /**< Start of print job, response required. */ PQD_CMD_JOBEND = 1 , /**< End of print job. */ PQD_CMD_START = 2 , /**< Start of printing (ignored). */ PQD_CMD_END = 3 , /**< End of printing (ignored). */ PQD_CMD_FILESTART = 4 , /**< Start of file printing (pc). */ PQD_CMD_FILEEND = 5 , /**< End of file printing (pc). */ PQD_CMD_INFO = 6 , /**< Information, response required. */ PQD_CMD_ACCT_CHECK = 7 , /**< Check permission, response required. */ PQD_CMD_ACCT_START = 8 , /**< Start of file printing (pc). */ PQD_CMD_ACCT_END = 9 , /**< End of file printing (pc). */ PQD_CMD_ACCT_CHARGE = 10 , /**< Summary for file printing (pc). */ PQD_CMD_CONTROL = 11 /**< Control request. */ }; %% module /** @file printqd.c The printqd daemon. A print accounting and quota daemon for use with the LPRng print system. */ #include "dk4conf.h" #include "dk4types.h" /* CORRECT NEXT LINE AFTER REPLACING PRINTQD */ #include "printqd.h" #if (DK4_CHAR_SIZE == 1) \ && ((DK4_HAVE_SIGACTION) || (DK4_HAVE_SIGSET)) \ && DK4_HAVE_GETPWNAM && DK4_HAVE_GETGRNAM \ && ((DK4_HAVE_SETSID) || (DK4_HAVE_SETPGRP)) \ && ((DK4_HAVE_PWD_H) && (DK4_HAVE_GRP_H)) \ && DK4_HAVE_UID_T && DK4_HAVE_GID_T && DK4_HAVE_MODE_T && (!DK4_ON_WINDOWS) #include #if DK4_HAVE_SYS_TYPES_H #ifndef SYS_TYPES_H_INCLUDED #include #define SYS_TYPES_H_INCLUDED 1 #endif #endif #if DK4_HAVE_SYS_STAT_H #ifndef SYS_STAT_H_INCLUDED #include #define SYS_STAT_H_INCLUDED 1 #endif #endif #if DK4_HAVE_FCNTL_H #ifndef FCNTL_H_INCLUDED #include #define FCNTL_H_INCLUDED 1 #endif #endif #if DK4_HAVE_ERRNO_H #ifndef ERRNO_H_INCLUDED #include #define ERRNO_H_INCLUDED 1 #endif #endif #if DK4_HAVE_UNISTD_H #ifndef UNISTD_H_INCLUDED #include #define UNISTD_H_INCLUDED 1 #endif #endif #if DK4_HAVE_PWD_H #ifndef PWD_H_INCLUDED #include #define PWD_H_INCLUDED 1 #endif #endif #if DK4_HAVE_GRP_H #ifndef GRP_H_INCLUDED #include #define GRP_H_INCLUDED 1 #endif #endif #if DK4_HAVE_SYSLOG_H #ifndef SYSLOG_H_INCLUDED #include #define SYSLOG_H_INCLUDED 1 #endif #endif #include "dk4opt.h" #include "dk4dmt.h" #include "dk4inst.h" #include "dk4dmt.h" #include "dk4sto.h" #include "dk4sock.h" #include "dk4mem.h" #include "dk4str8.h" #include "dk4mai8dii.h" #include "dk4mai8dsz.h" #include "dk4mai8dus.h" #include "dk4mai8ddu.h" #include "dk4mao8d.h" #include "dk4fopc8.h" #include "dk4dbi.h" #include "dk4dbit8.h" #include "dk4time.h" #include "dk4time08.h" #include "dk4mkdh8.h" #include "dk4delfile08.h" $!trace-include #ifndef LOG_LPR /** Default log feature for printing. */ #define LOG_LPR (6 << 3) #endif #ifndef LOG_EMERG /** Log message level to ignore, printqd does not make system unusable. */ #define LOG_EMERG (0) #endif #ifndef LOG_ERR /** Log message level error. */ #define LOG_ERR (3) #endif #ifndef LOG_INFO /** Log message level info. */ #define LOG_INFO (6) #endif /** Input buffer, used * for configuration file reading and * for reading requests. */ static char inbuf[1024]; /** Buffer to construct key for database entries. */ static char kb[sizeof(inbuf)]; /** Buffer to construct/convert values for database entries. */ static char vb[sizeof(inbuf)]; /** Options used by the program. */ static dk4_option_t options[] = { /* Debug mode. */ { { dkT('d'), dkT("debug"), DK4_OPT_ARG_NONE }, { NULL }, 0 }, /* Configuration file. */ { { dkT('c'), dkT("config"), DK4_OPT_ARG_STRING }, { NULL }, 0 } }; /** Default configuration file name. */ static const char def_conf_file_name[] = { DK4_INST_DIR_ETC "/printqd/printqd.conf" }; /** Default socket file name. */ static const char def_sock_file_name[] = { DK4_INST_DIR_VAR "/run/printqd/printqd.sock" }; /** Default database file name. */ static const char def_db_file_name[] = { #if DK4_HAVE_DB_H "bdb::" DK4_INST_DIR_VAR "/lib/printqd/printqd.db" #else #if DK4_HAVE_NDBM_H "ndbm::" DK4_INST_DIR_VAR "/lib/printqd/printqd" #else "mem::" DK4_INST_DIR_VAR "/lib/printqd/printqd.db" #endif #endif }; /** Default log file name. */ static const char def_log_file_name[] = { DK4_INST_DIR_VAR "/log/printqd/printqd.log" }; /** PID file name. */ static const char pid_file_name[] = { DK4_INST_DIR_VAR "/run/printqd/printqd.pid" }; /** Constant texts used by the module. */ static const char * const printqd_kw[] = { $!string-table # # 0 Program name # printqd # # 1 File open mode # r # # 2 Asterisk # * # # 3 unlimited # unlimited # # 4 denied # denied # # 5 Newline # \n # # 6 Start of comment # \# # # 7 8 # : : # # 9 # ERROR: # # 10 File open mode # a # # 11 Empty string # # # 12 13 14 # Not an integer number:\n" ", reading stoppend at " "! # # 15 # Positive number required! # # 16 17 18 # Not a size specification:\n" ", reading stopped at " "! # # 19 20 21 # Not a 16 bit unsigned number:\n" ", reading stopped at " "! # # 22 # Port number must not be 0! # # 23 24 # Too much text, unexpected:\n" "! # # 25 26 # Not a peer definition:\n" "! # # 27 # Not enough memory (allocation failed)! # # 28 29 30 # Not an unsigned number:\n" ", reading stopped at " "! # # 31 # Missing limit text! # # 32 33 # Limit redefinition for: " "! # # 34 # Redefinition of default limit! # # 35 # Redefinition of deny action! # # 36 37 # Printer already exists: " "! # # 38 39 # Printer alias already exists: " "! # # 40 41 # Class not found: " "! # # 42 # Redefinition of printers class! # # 43 44 # Class already exists: " "! # # 45 # Missing class name! # # 46 47 # Printer or alias already exists: " "! # # 48 # Missing printer name! # # 49 50 # Illegal section type name: " "! # # 51 # Missing section type! # # 52 # Redefinition of user ID to run as! # # 53 54 # User not found: " "! # # 55 # Only allowed in options section! # # 56 # Redefinition of group ID to run as! # # 57 58 # Group not found: " "! # # 59 # Redefinition of database name! # # 60 # Redefinition of socket name! # # 61 # Only allowed in class section! # # 62 # Only allowed in printer section! # # 63 # Redefinition of log file name! # # 64 # Value missing! # # 65 # Failed to open file for reading! # # 66 # User to run as was changed! # # 67 # Group to run as was changed! # # 68 # Failed to install signal handlers! # # 69 # Failed to restore signal handlers! # # 70 # : ERROR: Failed to create new process!\n # # 71 # Failed to create new process! # # 72 # Service start prevented by errors! # # 73 74 # Daemon starting. Daemon exited. # # 75 76 77 78 # Entering service mode. Exiting service mode. Exiting service mode for reconfiguration. Exited service mode. # # 79 # o:test # # 80 81 82 83 84 Response keywords # ACCEPT REMOVE HOLD 0 0 0 0 -1 # # 85 Space # # # 86 87 Markers for request and response # <- -> # # 88 89 90 Start of db keys # p: a: j: # # 91 92 Error: No class for printer # No class configured for printer: " "! # # 93 94 # Warning: No default limit configured for class: " "! # # 95 96 # Failed to create directory hierarchy for log file:\n" "! # # 97 98 # Failed to create directory hierarchy for database:\n" "! # # 99 100 # Failed to create directory hierarchy for socket:\n" "! # # 101 102 # Failed to listen on local socket:\n" "! # # 103 # Failed to create TCP listener socket set! # # 104 # Failed to create UDP socket set! # # 105 106 # Failed to open database:\n" "! # # 107 # Failed to write initial entry to database! # # 108 # Failed to synchronize database to disk! # # 109 # Failed to change log file ownership! # # 110 # Failed to change log file permissions! # # 111 # Failed to change database ownership and/or permissions! # # 112 # Failed to change local socket ownership! # # 113 # Failed to change local socket permissions! # # 114 # Failed to switch group! # # 115 # Failed to switch user! # # 116 # Response text too long (bug)! # # 117 # Failed to write database entry! # # 118 # Failed to delete database entry! # # 119 # The select() function failed! # # 120 # Insufficient memory! # # 121 # Failed to synchronize database to disk! # # 122 123 124 # DB SET " "=" " ok. # # 125 ... 132 # " FAILED (invalid arguments)! " FAILED (syntax)! " FAILED (numeric overflow in size calculation)! " FAILED (unsupported db backend)! " FAILED (no such entry)! " FAILED (insufficient memory)! " FAILED (can not write)! " FAILED (reason unknown)! # # 133 # DB GET " # # 134 # " FAILED (buffer too small)! # # 135 # DB DEL " # # 136 ... 144 # DB SYN ok. DB SYN FAILED (invalid arguments)! DB SYN FAILED (unsupported db backend)! DB SYN FAILED (syntax)! DB SYN FAILED (security checks for file failed)! DB SYN FAILED (can not open file for writing)! DB SYN FAILED (can not write)! DB SYN FAILED (can not close file)! DB SYN FAILED (unknown reason)! # # 145 ... 151 # (l= ,u= ,a= ) + u= => (l= ) unlimited # # 152 # p # # 153 # ) + a= # # 154 155 # Illegal log feature name: " "! # # 156 157 158 # Failed to update account data for user " " in class " "! # # 159 160 # Missing class name in reset request! Missing user name in reset request! # # 161 162 163 # Failed to update account data for user " " in class " "! # # 164 165 # No such class: " "! # # 166 167 # Not a number: " "! # # 168 169 # Illegal sub-command: " "! # # 170 171 # Logging to file " " failed! # # 172 # Database modification failed! # # 173 # Database synchronization to disk failed! # # 174 175 # Failed to change ownership on database file:\n" "! # # 176 177 # Failed to change permissions on database file:\n" "! # # 178 # " (name too long)! # # 179 # " (file exists, but no socket)! # # 180 # " (failed to remove old socket)! # # 181 182 # " (failed to create socket: )! # # 183 # " (failed to bind local # # 184 # " (failed to listen # # 185 186 # " (failed to change ownership)! " (failed to change permissions)! # # 187 # Daemon exited due to errors, see log file! # # 188 189 # Failed to create TCP socket set (math overflow)! Failed to create TCP socket set (memory)! # # 190 191 192 193 194 # Failed to create TCP socket set (socket(): )! Failed to create TCP socket set (bind(): Failed to create TCP socket set (listen(): Failed to create TCP socket set (getaddrinfo(): # # 195 196 # Failed to create UDP socket set (math overflow)! Failed to create UDP socket set (memory)! # # 197 ... 201 # Failed to create UDP socket set (socket(): )! Failed to create UDP socket set (bind(): Failed to create UDP socket set (listen(): Failed to create UDP socket set (getaddrinfo(): # # 202 ... 209 # " (backend type not supported)! " (overflow in size calculation)! " (insufficient memory)! "\n(not a regular file / not a database / key or value size too large)! " (denied by security check)! " (failed to open for reading)! " (failed to write to file)! " (failed to synchronize to disk)! # # 210 ... 216 # Failed to write initial entry to database (key or value too long)! Failed to write initial entry to database (unsupported backend type)! Failed to write initial entry to database (insufficient memory)! Failed to write initial entry to database (can not open file for writing)! Failed to write initial entry to database (can not write to file)! Failed to write initial entry to database (can not synchronize to disk)! Failed to write initial entry to database (denied by security check)! # # 217 218 # Failed to change PID file ownership! Failed to change PID file permissions! $!end }; /** Choices for deny action. */ static const char * const deny_action_names[] = { $!string-table remove hold $!end }; /** Section names in configuration files. */ static const char * const config_section_names[] = { $!string-table options class printer $!end }; /** Option names in configuration file. */ static const char * const config_option_names[] = { $!string-table # # 0 1 # run as user run as group # # 2 # database # # 3 4 5 # local socket local socket backlog max local connections # # 6 # udp port # # 7 8 9 # tcp port tcp port backlog max tcp connections # # 10 11 12 # info allow data allow admin allow # # 13 14 15 # user limit group limit default limit # # 16 # deny action # # 17 18 # alias class # # 19 20 # log file log features # # 21 22 # local socket owner local socket group $!end }; /** Command names in the printqd protocol. */ static const char * const pqd_command_names[] = { $!string-table jobstart jobend start end filestart fileend info acct-check acct-start acct-end acct-charge control $!end }; /** Sub-commands of control. */ static const char * const control_sub_cmds[] = { $!string-table r$eset a$dd d$atabase-cleanup $!end }; /** Argument names for control requests. */ static const char * const control_arg_names[] = { $!string-table c$lass u$ser p$ages $!end }; /** Log feature names. */ static const char * const log_feature_names[] = { $!string-table db info all * none - $!end }; static const char * const db_type_names[] = { $!string-table p a j $!end }; /** Daemon configuration. */ static pqd_conf_t conf; /** Connections over local socket, stores pqd_l_conn_t. Not NULL in service (service is not run if allocation fails). */ static dk4_sto_t *s_c_unix = NULL; /** Iterator through s_c_unix container. Not NULL in service (service is not run if allocation failes). */ static dk4_sto_it_t *i_c_unix = NULL; /** Container for TCP connections, stores pqd_n_conn_t. Not NULL in service (service is not run if allocation fails). */ static dk4_sto_t *s_c_tcp = NULL; /** Iterator through s_c_tcp container. Not NULL in service (service is not run if allocation fails). */ static dk4_sto_it_t *i_c_tcp = NULL; /** To accept TCP connections. May be NULL in service, if no TCP port number is conigured. */ static dk4_socket_set_t *ss_tcp = NULL; /** For info requests. May be NULL in service, if no UDP port number is configured. */ static dk4_socket_set_t *ss_udp = NULL; /** Database. Not NULL in service (service is not run if allocation fails). */ static dk4_dbi_t *db = NULL; /** Number of options in the options array. */ static const size_t szoptions = sizeof(options)/sizeof(dk4_option_t); /** Current connections on local socket. */ static size_t con_loc = 0; /** Current connections on TCP socket. */ static size_t con_tcp = 0; /** Timestamp of previous log message. */ static dk4_time_t prev_log_time = (dk4_time_t)0UL; /** Listen for incoming UNIX connection requests. Always set in service, service exits when failed to bind local address. */ static dk4_socket_t so_unix = INVALID_SOCKET; /** Program exit code, either EXIT_FAILURE or EXIT_SUCCESS. */ static int exval = EXIT_FAILURE; /** Flag: can continue outer (and inner) loop, 1=yes, 0=no, -1=abort. */ static int ccouter = 1; /** Flag: can continue inner loop, 1=yes, 0=no, -1=abort. */ static int ccinner = 1; /** Flag: User and group switching was already done. */ static int user_switched = 0; /** Syslog feature to use. */ static int syslogf = LOG_LPR; /** Flag: Modified since last synchronization. */ static int db_modified = 0; /** Flag: Debug mode, process runs in foreground and logs to stderr. */ static int debug = 0; /** Flag: Previous attempt to log to file failed. */ static int prev_filelog_failed = 0; /** Flag: Previous database modification failed. */ static int prev_dbmod_failed = 0; /** Flag: Previous database synchronization to disk failed. */ static int prev_dbsync_failed = 0; /** UID in previous pass. */ static uid_t prev_uid = (uid_t)0; /** GID in previous pass. */ static gid_t prev_gid = (gid_t)0; #ifdef SIGPIPE /** Indicator: SIGPIPE signal received. */ static DK4_VOLATILE dk4_sig_atomic_t sig_had_pipe = 0; #endif #ifdef SIGHUP /** Indicator: SIGHUP signal received. */ static DK4_VOLATILE dk4_sig_atomic_t sig_had_hup = 0; #endif /** Indicator: SIGINT signal received. */ static DK4_VOLATILE dk4_sig_atomic_t sig_had_int = 0; /** Indicator: SIGTERM signal received. */ static DK4_VOLATILE dk4_sig_atomic_t sig_had_term = 0; /** Pass pointer through. Use of this function is recommended by CERT C coding standard to avoid optimizing out. @param ptr Pointer to pass through. @return The ptr argument. */ static DK4_VOLATILE dk4_sig_atomic_t * sig_pass_pointer(DK4_VOLATILE dk4_sig_atomic_t *ptr) { return ptr; } #ifdef SIGPIPE /** Handler for SIGPIPE signal. @param signo Signal number (always SIGPIPE, ignored). */ static void sig_handler_pipe(int signo) { *sig_pass_pointer(&sig_had_pipe) = 1; } #endif #ifdef SIGHUP /** Handler for SIGHUP signal. @param signo Signal number (always SIGHUP, ignored). */ static void sig_handler_hup(int signo) { *sig_pass_pointer(&sig_had_hup) = 1; } #endif /** Handler for SIGINT signal. @param signo Signal number (always SIGINT, ignored). */ static void sig_handler_int(int signo) { *sig_pass_pointer(&sig_had_int) = 1; } /** Handler for SIGTERM signal. @param signo Signal number (always SIGTERM, ignored). */ static void sig_handler_term(int signo) { *sig_pass_pointer(&sig_had_term) = 1; } /** Read value from volatile atomic type. This function is necessary as some compilers mis-optimize direct access to volatile variables (at least if you believe one of the coding standards). @param ap Pointer to volatile atomic variable. @return Contents of the variable. */ static dk4_sig_atomic_t sig_read_atomic(DK4_VOLATILE dk4_sig_atomic_t *ap) { return (*ap); } /** Check whether we can continue or must abort due to signal. @param dopipe Flag: Check for SIGPIPE too. @return 1 if we can continue, 0 otherwise. */ static int can_continue(int dohup, int dopipe) { int back = 1; $? "+ can_continue dohup=%d dopipe=%d", dohup, dopipe if (0 != sig_read_atomic(&sig_had_term)) { back = 0; } if (0 != sig_read_atomic(&sig_had_int)) { back = 0; } #ifdef SIGHUP if (0 != dohup) { if (0 != sig_read_atomic(&sig_had_hup)) { back = 0; } if (1 > ccinner) { back = 0; } } #endif #ifdef SIGPIPE if (0 != dopipe) { if (0 != sig_read_atomic(&sig_had_pipe)) { back = 0; } } #endif if (1 > ccouter) { back = 0; } $? "- can_continue %d", back return back; } /** Log a multi part message to file, either stderr or log file. @param fipo Output file. @param timebuf Text buffer containing timestamp. @param sourcefile Source file name. @param linebuf Text buffer containing source file line number. @param error Flag: Insert keyword "ERROR:" @param shtime Flag: Show timestamp from buffer. @param shline Flag: Show line number from buffer. @param msgs Array containing the message parts. @param szmsgs Number of elements in msgs array. */ static void log_file_multipart_message( FILE *fipo, const char *timebuf, const char *sourcefile, const char *linebuf, int error, int shtime, int shline, const char * const *msgs, size_t szmsgs ) { size_t i; $? "+ log_file_multipart_message %u", (unsigned)szmsgs if (0 != shtime) { fputs(printqd_kw[6], fipo); fputs(timebuf, fipo); fputs(printqd_kw[5], fipo); } if (NULL != sourcefile) { fputs(sourcefile, fipo); if (0 != shline) { fputs(printqd_kw[8], fipo); fputs(linebuf, fipo); } fputs(printqd_kw[7], fipo); } if (0 != error) { fputs(printqd_kw[9], fipo); } for (i = 0; i < szmsgs; i++) { if (NULL != msgs[i]) { fputs(msgs[i], fipo); } } fputs(printqd_kw[5], fipo); fflush(fipo); $? "- log_file_multipart_message" } /** Write a log message consisting of multiple parts. @param sourcefile Configuration file name. @param sourceline Configuration file line number. @param sll Syslog level. @param msgs Array containing the message parts. @param szmsgs Number of elements in msgs array. */ static void log_multipart_message( const char *sourcefile, unsigned long sourceline, int sll, int error, char const * const *msgs, size_t szmsgs ) { char timebuf[64]; char linebuf[64]; FILE *fipo; const char *lfn; dk4_time_t current; int stests = DK4_FOPEN_SC_PRIVILEGED; int shtime = 0; int shline = 0; $? "+ log_multipart_message %u", (unsigned)szmsgs /* Check whether to prepend a timestamp line. */ dk4time_get(¤t); if (current != prev_log_time) { prev_log_time = current; if (0 != dk4time_as_text_c8(timebuf, sizeof(timebuf), ¤t, NULL)) { shtime = 1; } } /* Check whether to prepend source file name and line number. */ if ((NULL != sourcefile) && ((dk4_um_t)0UL != sourceline)) { shline = dk4ma_write_c8_decimal_unsigned( linebuf, sizeof(linebuf), (dk4_um_t)sourceline, 0, NULL ); } /* Output to log file. */ lfn = conf.lname; if (NULL == lfn) { lfn = def_log_file_name; } fipo = dk4fopen_c8(lfn, printqd_kw[10], stests, NULL); if (NULL != fipo) { prev_filelog_failed = 0; log_file_multipart_message( fipo, timebuf, sourcefile, linebuf, error, shtime, shline, msgs, szmsgs ); fclose(fipo); } else { #if DK4_HAVE_SYSLOG if (0 == prev_filelog_failed) { openlog(printqd_kw[0], LOG_PID, syslogf); syslog(LOG_ERR, "%s%s%s", printqd_kw[170], lfn, printqd_kw[171]); closelog(); } #endif prev_filelog_failed = 1; } /* Output to stderr when debugging. */ if (0 != debug) { log_file_multipart_message( stderr, timebuf, sourcefile, linebuf, error, shtime, shline, msgs, szmsgs ); } /* Output to syslog. */ if (LOG_EMERG != sll) { #if DK4_HAVE_SYSLOG openlog(printqd_kw[0], LOG_PID, syslogf); if (1 < szmsgs) { if (2 < szmsgs) { if (3 < szmsgs) { if (4 < szmsgs) { if (5 < szmsgs) { if (6 < szmsgs) { if (7 < szmsgs) { if (8 < szmsgs) { syslog( sll, "%s%s%s%s%s%s%s%s%s", ((NULL != msgs[0]) ? (msgs[0]) : (printqd_kw[11])), ((NULL != msgs[1]) ? (msgs[1]) : (printqd_kw[11])), ((NULL != msgs[2]) ? (msgs[2]) : (printqd_kw[11])), ((NULL != msgs[3]) ? (msgs[3]) : (printqd_kw[11])), ((NULL != msgs[4]) ? (msgs[4]) : (printqd_kw[11])), ((NULL != msgs[5]) ? (msgs[5]) : (printqd_kw[11])), ((NULL != msgs[6]) ? (msgs[6]) : (printqd_kw[11])), ((NULL != msgs[7]) ? (msgs[7]) : (printqd_kw[11])), ((NULL != msgs[8]) ? (msgs[8]) : (printqd_kw[11])) ); } else { syslog( sll, "%s%s%s%s%s%s%s%s", ((NULL != msgs[0]) ? (msgs[0]) : (printqd_kw[11])), ((NULL != msgs[1]) ? (msgs[1]) : (printqd_kw[11])), ((NULL != msgs[2]) ? (msgs[2]) : (printqd_kw[11])), ((NULL != msgs[3]) ? (msgs[3]) : (printqd_kw[11])), ((NULL != msgs[4]) ? (msgs[4]) : (printqd_kw[11])), ((NULL != msgs[5]) ? (msgs[5]) : (printqd_kw[11])), ((NULL != msgs[6]) ? (msgs[6]) : (printqd_kw[11])), ((NULL != msgs[7]) ? (msgs[7]) : (printqd_kw[11])) ); } } else { syslog( sll, "%s%s%s%s%s%s%s", ((NULL != msgs[0]) ? (msgs[0]) : (printqd_kw[11])), ((NULL != msgs[1]) ? (msgs[1]) : (printqd_kw[11])), ((NULL != msgs[2]) ? (msgs[2]) : (printqd_kw[11])), ((NULL != msgs[3]) ? (msgs[3]) : (printqd_kw[11])), ((NULL != msgs[4]) ? (msgs[4]) : (printqd_kw[11])), ((NULL != msgs[5]) ? (msgs[5]) : (printqd_kw[11])), ((NULL != msgs[6]) ? (msgs[6]) : (printqd_kw[11])) ); } } else { syslog( sll, "%s%s%s%s%s%s", ((NULL != msgs[0]) ? (msgs[0]) : (printqd_kw[11])), ((NULL != msgs[1]) ? (msgs[1]) : (printqd_kw[11])), ((NULL != msgs[2]) ? (msgs[2]) : (printqd_kw[11])), ((NULL != msgs[3]) ? (msgs[3]) : (printqd_kw[11])), ((NULL != msgs[4]) ? (msgs[4]) : (printqd_kw[11])), ((NULL != msgs[5]) ? (msgs[5]) : (printqd_kw[11])) ); } } else { syslog( sll, "%s%s%s%s%s", ((NULL != msgs[0]) ? (msgs[0]) : (printqd_kw[11])), ((NULL != msgs[1]) ? (msgs[1]) : (printqd_kw[11])), ((NULL != msgs[2]) ? (msgs[2]) : (printqd_kw[11])), ((NULL != msgs[3]) ? (msgs[3]) : (printqd_kw[11])), ((NULL != msgs[4]) ? (msgs[4]) : (printqd_kw[11])) ); } } else { syslog( sll, "%s%s%s%s", ((NULL != msgs[0]) ? (msgs[0]) : (printqd_kw[11])), ((NULL != msgs[1]) ? (msgs[1]) : (printqd_kw[11])), ((NULL != msgs[2]) ? (msgs[2]) : (printqd_kw[11])), ((NULL != msgs[3]) ? (msgs[3]) : (printqd_kw[11])) ); } } else { syslog( sll, "%s%s%s", ((NULL != msgs[0]) ? (msgs[0]) : (printqd_kw[11])), ((NULL != msgs[1]) ? (msgs[1]) : (printqd_kw[11])), ((NULL != msgs[2]) ? (msgs[2]) : (printqd_kw[11])) ); } } else { syslog( sll, "%s%s", ((NULL != msgs[0]) ? (msgs[0]) : (printqd_kw[11])), ((NULL != msgs[1]) ? (msgs[1]) : (printqd_kw[11])) ); } } else { syslog( sll, "%s", ((NULL != msgs[0]) ? (msgs[0]) : (printqd_kw[11])) ); } closelog(); #endif } $? "- log_multipart_message" } static void log_1( const char *sourcefile, unsigned long sourceline, int sll, int error, size_t i1 ) { const char *msgs[2]; $? "+ log_1" msgs[0] = printqd_kw[i1]; msgs[1] = NULL; log_multipart_message(sourcefile, sourceline, sll, error, msgs, 1); $? "- log_1" } static void log_3( const char *sourcefile, unsigned long sourceline, int sll, int error, size_t i1, size_t i2, const char *s1 ) { const char *msgs[4]; $? "+ log_3" msgs[0] = printqd_kw[i1]; msgs[1] = s1; msgs[2] = printqd_kw[i2]; msgs[3] = NULL; log_multipart_message(sourcefile, sourceline, sll, error, msgs, 3); $? "- log_3" } static void log_5( const char *sourcefile, unsigned long sourceline, int sll, int error, size_t i1, size_t i2, size_t i3, const char *s1, const char *s2 ) { const char *msgs[6]; $? "+ log_5" msgs[0] = printqd_kw[i1]; msgs[1] = s1; msgs[2] = printqd_kw[i2]; msgs[3] = s2; msgs[4] = printqd_kw[i3]; msgs[5] = NULL; log_multipart_message(sourcefile, sourceline, sll, error, msgs, 5); $? "- log_5" } /** Set database entry (create or modify). @param db Database to modify. @param k Key. @param v Value. @param erp Error report, may be NULL. @return 1 on success, 0 on error. */ static int dbi_set( dk4_dbi_t *db, const char *k, const char *v, dk4_er_t *erp ) { dk4_er_t er; int back = 0; dk4error_init(&er); back = dk4dbi_c8_set(db, k, v, &er); dk4error_copy(erp, &er); db_modified = 1; if (0 != back) { prev_dbmod_failed = 0; if (0 != conf.ldb) { log_5(NULL, 0UL, LOG_EMERG, 0, 122, 123, 124, k, v); } } else { #if DK4_HAVE_SYSLOG if (0 == prev_dbmod_failed) { openlog(printqd_kw[0], LOG_PID, syslogf); syslog(LOG_ERR, "%s", printqd_kw[172]); closelog(); } #endif prev_dbmod_failed = 1; switch (er.ec) { case DK4_E_INVALID_ARGUMENTS : { log_5(NULL, 0UL, LOG_EMERG, 1, 122, 123, 125, k, v); } break; case DK4_E_SYNTAX : { log_5(NULL, 0UL, LOG_EMERG, 1, 122, 123, 126, k, v); } break; case DK4_E_MATH_OVERFLOW : { log_5(NULL, 0UL, LOG_EMERG, 1, 122, 123, 127, k, v); } break; case DK4_E_NOT_SUPPORTED : { log_5(NULL, 0UL, LOG_EMERG, 1, 122, 123, 128, k, v); } break; case DK4_E_NOT_FOUND : { log_5(NULL, 0UL, LOG_EMERG, 1, 122, 123, 129, k, v); } break; case DK4_E_MEMORY_ALLOCATION_FAILED : { log_5(NULL, 0UL, LOG_EMERG, 1, 122, 123, 130, k, v); } break; case DK4_E_WRITE_FAILED : { log_5(NULL, 0UL, LOG_EMERG, 1, 122, 123, 131, k, v); } break; default : { log_5(NULL, 0UL, LOG_EMERG, 1, 122, 123, 132, k, v); } break; } } return back; } /** Retrieve database entry. @param db Database to retrieve data from. @param k Key text. @param vbuf Buffer for text value. @param vsz Size of buffer. @param erp Error report, may be NULL. @return 1 on success, 0 on error. */ static int dbi_get( dk4_dbi_t *db, const char *k, char *vbuf, size_t vsz, dk4_er_t *erp ) { dk4_er_t er; int back = 0; dk4error_init(&er); back = dk4dbi_c8_get(db, k, vbuf, vsz, &er); dk4error_copy(erp, &er); if (0 != back) { if (0 != conf.ldb) { log_5(NULL, 0UL, LOG_EMERG, 0, 133, 123, 124, k, vbuf); } } else { switch (er.ec) { case DK4_E_INVALID_ARGUMENTS : { log_3(NULL, 0UL, LOG_EMERG, 1, 133, 125, k); } break; case DK4_E_MATH_OVERFLOW : { log_3(NULL, 0UL, LOG_EMERG, 1, 133, 127, k); } break; case DK4_E_NOT_SUPPORTED : { log_3(NULL, 0UL, LOG_EMERG, 1, 133, 128, k); } break; case DK4_E_NOT_FOUND : { if (0 != conf.ldb) { log_3(NULL, 0UL, LOG_EMERG, 1, 133, 129, k); } } break; case DK4_E_BUFFER_TOO_SMALL : { log_3(NULL, 0UL, LOG_EMERG, 1, 133, 134, k); } break; default : { log_3(NULL, 0UL, LOG_EMERG, 1, 133, 132, k); } break; } } return back; } /** Delete DB entry if it exists. @param db Database to modify. @param k Key. @param erp Error report, may be NULL. @return 1 on success, 0 on error. */ static int dbi_del( dk4_dbi_t *db, const char *k, dk4_er_t *erp ) { dk4_er_t er; int back = 0; dk4error_init(&er); back = dk4dbi_c8_del(db, k, &er); dk4error_copy(erp, &er); db_modified = 1; if (0 != back) { prev_dbmod_failed = 0; if (0 != conf.ldb) { log_3(NULL, 0UL, LOG_EMERG, 0, 135, 124, k); } } else { if (DK4_E_NOT_FOUND != er.ec) { #if DK4_HAVE_SYSLOG if (0 == prev_dbmod_failed) { openlog(printqd_kw[0], LOG_PID, syslogf); syslog(LOG_ERR, "%s", printqd_kw[172]); closelog(); } #endif prev_dbmod_failed = 1; } switch (er.ec) { case DK4_E_INVALID_ARGUMENTS : { log_3(NULL, 0UL, LOG_EMERG, 1, 135, 125, k); } break; case DK4_E_SYNTAX : { log_3(NULL, 0UL, LOG_EMERG, 1, 135, 126, k); } break; case DK4_E_MATH_OVERFLOW : { log_3(NULL, 0UL, LOG_EMERG, 1, 135, 127, k); } break; case DK4_E_NOT_SUPPORTED : { log_3(NULL, 0UL, LOG_EMERG, 1, 135, 128, k); } break; case DK4_E_WRITE_FAILED : { log_3(NULL, 0UL, LOG_EMERG, 1, 135, 131, k); } break; case DK4_E_NOT_FOUND : { if (0 != conf.ldb) { log_3(NULL, 0UL, LOG_EMERG, 1, 135, 129, k); } } break; default : { log_3(NULL, 0UL, LOG_EMERG, 1, 135, 132, k); } break; } } return back; } /** Synchronize database modifications to disk, if any. @param db Database. @param erp Error report, may be NULL. @return 1 on success, 0 on error. */ static int dbi_sync(dk4_dbi_t *db, dk4_er_t *erp) { dk4_er_t er; int back = 0; if (0 != db_modified) { dk4error_init(&er); back = dk4dbi_sync(db, &er); dk4error_copy(erp, &er); if (0 != back) { db_modified = 0; prev_dbsync_failed = 0; if (0 != conf.ldb) { log_1(NULL, 0UL, LOG_EMERG, 0, 136); } } else { #if DK4_HAVE_SYSLOG if (0 == prev_dbsync_failed) { openlog(printqd_kw[0], LOG_PID, syslogf); syslog(LOG_ERR, "%s", printqd_kw[173]); closelog(); } #endif prev_dbsync_failed = 1; switch (er.ec) { case DK4_E_INVALID_ARGUMENTS : { log_1(NULL, 0UL, LOG_EMERG, 1, 137); } break; case DK4_E_NOT_SUPPORTED : { log_1(NULL, 0UL, LOG_EMERG, 1, 138); } break; case DK4_E_SYNTAX : { log_1(NULL, 0UL, LOG_EMERG, 1, 139); } break; case DK4_E_SEC_CHECK : { log_1(NULL, 0UL, LOG_EMERG, 1, 140); } break; case DK4_E_OPEN_WRITE_FAILED : { log_1(NULL, 0UL, LOG_EMERG, 1, 141); } break; case DK4_E_WRITE_FAILED : { log_1(NULL, 0UL, LOG_EMERG, 1, 142); } break; case DK4_E_CLOSE_FAILED : { log_1(NULL, 0UL, LOG_EMERG, 1, 143); } break; default : { log_1(NULL, 0UL, LOG_EMERG, 1, 144); } break; } } } else { back = 1; } return back; } /** Compare two pqd_l_conn_t structures. @param l Left object. @param r Right object. @param cr Comparison criteria, ignored. @return Comparison result. */ static int compare_pqd_l_conn_t(const void *l, const void *r, int cr) { const pqd_l_conn_t *pl; const pqd_l_conn_t *pr; int back = 0; $? "+ compare_pqd_l_conn_t" if (NULL != l) { if (NULL != r) { pl = (const pqd_l_conn_t *)l; pr = (const pqd_l_conn_t *)r; if (pl->sock > pr->sock) { back = 1; } else { if (pl->sock < pr->sock) { back = -1; } } } else { back = 1; } } else { if (NULL != r) { back = -1; } } $? "- compare_pqd_l_conn_t %d", back return back; } /** Compare two pqd_n_conn_t structures. @param l Left object. @param r Right object. @param cr Comparison criteria, ignored. @return Comparison result. */ static int compare_pqd_n_conn_t(const void *l, const void *r, int cr) { const pqd_n_conn_t *pl; const pqd_n_conn_t *pr; int back = 0; $? "+ compare_pqd_n_conn_t" if (NULL != l) { if (NULL != r) { pl = (const pqd_n_conn_t *)l; pr = (const pqd_n_conn_t *)r; if (pl->sock > pr->sock) { back = 1; } else { if (pl->sock < pr->sock) { back = -1; } } } else { back = 1; } } else { if (NULL != r) { back = -1; } } $? "- compare_pqd_n_conn_t %d", back return back; } /** Delete a limit structure (release memory). @param ptr Limit to delete. */ static void limit_delete(pqd_limit_t *ptr) { $? "+ limit_delete %s", TR_8PTR(ptr) if (NULL != ptr) { $? ". name = \"%s\"", TR_8STR(ptr->name) dk4mem_release(ptr->name); ptr->limit = (dk4_um_t)0UL; dk4mem_free(ptr); } $? "- limit_delete" } /** Create limit structure (allocate memory). @param name User or group name. @param max Maximum page number. @return Valid pointer on success, NULL on error. */ static pqd_limit_t * limit_new(const char *name, dk4_um_t max) { pqd_limit_t *back = NULL; $? "+ limit_new \"%s\" %lu", TR_8STR(name), (unsigned long)max if (NULL != name) { $? ". name ok" back = dk4mem_new(pqd_limit_t,1,NULL); if (NULL != back) { $? ". allocation ok" back->limit = max; back->name = dk4str8_dup(name, NULL); if (NULL == back->name) { $? "! allocation (2)" limit_delete(back); back = NULL; } } #if TRACE_DEBUG else { $? "! allocation (1)" } #endif } $? "- limit_new %s", TR_8PTR(back) return back; } /** Compare limit structures. @param l Left object. @param r Right object. @param cr Comparison criteria (0=limit/limit, 1=limit/name). @return Comparison result. */ static int limit_compare(const void *l, const void *r, int cr) { const pqd_limit_t *pl; const pqd_limit_t *pr; $? "+ limit_compare" int back = 0; if (NULL != l) { if (NULL != r) { pl = (const pqd_limit_t *)l; switch (cr) { case 1: { if (NULL != pl->name) { back = strcmp(pl->name, (const char *)r); if (-1 > back) { back = -1; } if ( 1 < back) { back = 1; } } else { back = -1; } } break; default: { pr = (const pqd_limit_t *)r; if (NULL != pl->name) { if (NULL != pr->name) { back = strcmp(pl->name, pr->name); if (-1 > back) { back = -1; } if ( 1 < back) { back = 1; } } else { back = 1; } } else { if (NULL != pr->name) { back = -1; } } } break; } } else { back = 1; } } else { if (NULL != r) { back = -1; } } $? "- limit_compare %d", back return back; } /** Delete all limits in a storage. @param it Storage iterator. */ static void limit_all_delete(dk4_sto_it_t *it) { pqd_limit_t *ptr; $? "+ limit_all_delete %s", TR_8PTR(it) dk4sto_it_reset(it); do { ptr = (pqd_limit_t *)dk4sto_it_next(it); if (NULL != ptr) { limit_delete(ptr); } } while (NULL != ptr); dk4sto_it_close(it); $? "- limit_all_delete" } /** Delete a class structure (release memory). @param ptr Class to delete. */ static void class_delete(pqd_class_t *ptr) { $? "+ class_delete %s", TR_8PTR(ptr) if (NULL != ptr) { $? ". ptr ok" dk4mem_release(ptr->name); if (NULL != ptr->i_u) { limit_all_delete(ptr->i_u); } ptr->i_u = NULL; if (NULL != ptr->s_u) { dk4sto_close(ptr->s_u); } ptr->s_u = NULL; if (NULL != ptr->i_g) { limit_all_delete(ptr->i_g); } ptr->i_g = NULL; if (NULL != ptr->s_g) { dk4sto_close(ptr->s_g); } ptr->s_g = NULL; ptr->dl = (dk4_um_t)0UL; ptr->da = 0; dk4mem_free(ptr); } #if TRACE_DEBUG else { $? "! ptr" } #endif $? "- class_delete" } /** Delete all classes from a containter. @param it Container iterator. */ static void class_all_delete(dk4_sto_it_t *it) { pqd_class_t *ptr; $? "+ class_all_delete %s", TR_8PTR(it) dk4sto_it_reset(it); do { ptr = (pqd_class_t *)dk4sto_it_next(it); if (NULL != ptr) { class_delete(ptr); } } while (NULL != ptr); dk4sto_it_close(it); $? "- class_all_delete" } /** Create a class structure (allocate memory). @param n Class name. @return Valid pointer on success, NULL on error. */ static pqd_class_t * class_new(const char *n) { pqd_class_t *back = NULL; int ok = 0; $? "+ class_new \"%s\"", TR_8STR(n) if (NULL != n) { $? ". n ok" back = dk4mem_new(pqd_class_t,1,NULL); if (NULL != back) { back->s_u = NULL; back->i_u = NULL; back->s_g = NULL; back->i_g = NULL; back->dl = (dk4_um_t)0UL; back->da = 0; back->hdl = 0; back->name = dk4str8_dup(n, NULL); if (NULL != back->name) { back->s_u = dk4sto_open(NULL); if (NULL != back->s_u) { dk4sto_set_comp(back->s_u, limit_compare, 0); back->i_u = dk4sto_it_open(back->s_u, NULL); if (NULL != back->i_u) { back->s_g = dk4sto_open(NULL); if (NULL != back->s_g) { dk4sto_set_comp(back->s_g, limit_compare, 0); back->i_g = dk4sto_it_open(back->s_g, NULL); if (NULL != back->i_g) { ok = 1; $? ". allocations ok" } #if TRACE_DEBUG else { $? "! allocation (6)" } #endif } #if TRACE_DEBUG else { $? "! allocation (5)" } #endif } #if TRACE_DEBUG else { $? "! allocation (4)" } #endif } #if TRACE_DEBUG else { $? "! allocation (3)" } #endif } #if TRACE_DEBUG else { $? "! allocation (2)" } #endif if (0 == ok) { $? "! allocations" class_delete(back); back = NULL; } } #if TRACE_DEBUG else { $? "! allocation (1)" } #endif } #if TRACE_DEBUG else { $? "! n" } #endif $? "- class_new %s", TR_8PTR(back) return back; } /** Compare classes. @param l Left object. @param r Right object. @param cr Comparison criteria (0=class/class, 1=class/name). @return Comparison result. */ static int class_compare(const void *l, const void *r, int cr) { const pqd_class_t *pl; const pqd_class_t *pr; int back = 0; $? "+ class_compare" if (NULL != l) { if (NULL != r) { pl = (const pqd_class_t *)l; switch (cr) { case 1: { if (NULL != pl->name) { back = strcmp(pl->name, (const char *)r); if (-1 > back) { back = -1; } if ( 1 < back) { back = 1; } } else { back = -1; } } break; default: { pr = (const pqd_class_t *)r; if (NULL != pl->name) { if (NULL != pr->name) { back = strcmp(pl->name, pr->name); if (-1 > back) { back = -1; } if ( 1 < back) { back = 1; } } else { back = 1; } } else { if (NULL != pr->name) { back = -1; } } } break; } } else { back = 1; } } else { if (NULL != r) { back = -1; } } $? "- class_compare %d", back return back; } /** Delete a printer structure (release memory). @param ptr Printer structure to delete. */ static void printer_delete(pqd_printer_t *ptr) { $? "+ printer_delete %s", TR_8PTR(ptr) if (NULL != ptr) { $? ". name = \"%s\"", TR_8STR(ptr->name) dk4mem_release(ptr->name); ptr->cl = NULL; dk4mem_free(ptr); } $? "- printer_delete" } /** Create printer structure (allocate memory). @param n Printer name. @return Valid pointer on success, NULL on error. */ static pqd_printer_t * printer_new(const char *n) { pqd_printer_t *back = NULL; $? "+ printer_new \"%s\"", TR_8STR(n) if (NULL != n) { back = dk4mem_new(pqd_printer_t,1,NULL); if (NULL != back) { back->cl = NULL; back->name = dk4str8_dup(n,NULL); if (NULL == back->name) { $? "! allocation (2)" printer_delete(back); back = NULL; } } #if TRACE_DEBUG else { $? "! allocation (1)" } #endif } #if TRACE_DEBUG else { $? "! n" } #endif $? "- printer_new %s", TR_8PTR(back) return back; } /** Compare two printers. @param l Left object. @param r Right object. @param cr Comparison criteria (0=printer/printer, 1=printer/name). @return Comparison result. */ static int printer_compare(const void *l, const void *r, int cr) { const pqd_printer_t *pl; const pqd_printer_t *pr; int back = 0; $? "+ printer_compare" if (NULL != l) { if (NULL != r) { pl = (const pqd_printer_t *)l; switch (cr) { case 1: { if (NULL != pl->name) { back = strcmp(pl->name, (const char *)r); if (-1 > back) { back = -1; } if ( 1 < back) { back = 1; } } else { back = -1; } } break; default: { pr = (const pqd_printer_t *)r; if (NULL != pl->name) { if (NULL != pr->name) { back = strcmp(pl->name, pr->name); if (-1 > back) { back = -1; } if ( 1 < back) { back = 1; } } else { back = 1; } } else { if (NULL != pr->name) { back = -1; } } } break; } } else { back = 1; } } else { if (NULL != r) { back = -1; } } $? "- printer_compare %d", back return back; } /** Delete all printers from a container. @param it Container iterator. */ static void printer_all_delete(dk4_sto_it_t *it) { pqd_printer_t *ptr; $? "+ printer_all_delete %s", TR_8PTR(it) dk4sto_it_reset(it); do { ptr = (pqd_printer_t *)dk4sto_it_next(it); if (NULL != ptr) { printer_delete(ptr); } } while (NULL != ptr); dk4sto_it_close(it); $? "- printer_all_delete" } /** Delete printer alias structure (release memory). @param ptr Structure to delete. */ static void printer_alias_delete(pqd_printer_alias_t *ptr) { $? "+ printer_alias_delete %s", TR_8PTR(ptr) if (NULL != ptr) { $? ". name = \"%s\"", TR_8STR(ptr->name) dk4mem_release(ptr->name); ptr->pr = NULL; dk4mem_free(ptr); } $? "- printer_alias_delete" } /** Create printer alias structure (allocate memory). @param n Alias name. @param pr Printer for alias. @return Valid pointer on success, NULL on error. */ static pqd_printer_alias_t * printer_alias_new(const char *n, pqd_printer_t *pr) { pqd_printer_alias_t *back = NULL; $? "+ printer_alias_new \"%s\" %s", TR_8STR(n), TR_8PTR(pr) if ((NULL != n) && (NULL != pr)) { back = dk4mem_new(pqd_printer_alias_t,1,NULL); if (NULL != back) { back->pr = pr; back->name = dk4str8_dup(n,NULL); if (NULL == back->name) { printer_alias_delete(back); back = NULL; } #if TRACE_DEBUG else { $? "! allocation (2)" } #endif } #if TRACE_DEBUG else { $? "! allocation (1)" } #endif } #if TRACE_DEBUG else { $? "! n or pr" } #endif $? "- printer_alias_new %s", TR_8PTR(back) return back; } /** Compare printer alias structures. @param l Left object. @param r Right object. @param cr Comparison criteria (0=alias/alias, 1=alias/name). @return Comparison result. */ static int printer_alias_compare(const void *l, const void *r, int cr) { const pqd_printer_alias_t *pl; const pqd_printer_alias_t *pr; int back = 0; $? "+ printer_alias_compare" if (NULL != l) { if (NULL != r) { pl = (const pqd_printer_alias_t *)l; switch (cr) { case 1: { if (NULL != pl->name) { back = strcmp(pl->name, (const char *)r); if (-1 > back) { back = -1; } if ( 1 < back) { back = 1; } } else { back = -1; } } break; default: { pr = (const pqd_printer_alias_t *)r; if (NULL != pl->name) { if (NULL != pr->name) { back = strcmp(pl->name, pr->name); if (-1 > back) { back = -1; } if ( 1 < back) { back = 1; } } else { back = 1; } } else { if (NULL != pr->name) { back = -1; } } } break; } } else { back = 1; } } else { if (NULL != r) { back = -1; } } $? "- printer_alias_compare %d", back return back; } /** Delete all printer alias structures in a container. @param it Container iterator. */ static void printer_alias_all_delete(dk4_sto_it_t *it) { pqd_printer_alias_t *ptr; $? "+ printer_alias_all_delete %s", TR_8PTR(it) dk4sto_it_reset(it); do { ptr = (pqd_printer_alias_t *)dk4sto_it_next(it); if (NULL != ptr) { printer_alias_delete(ptr); } } while (NULL != ptr); dk4sto_it_close(it); $? "- printer_alias_all_delete" } /** Initialize configuration before reading configuration file. */ static void config_init(void) { $? "+ config_init" DK4_MEMRES(&conf, sizeof(conf)); conf.sname = NULL; conf.dname = NULL; conf.lname = NULL; conf.s_c = NULL; conf.i_c = NULL; conf.s_p = NULL; conf.i_p = NULL; conf.s_a = NULL; conf.i_a = NULL; conf.s_ai = NULL; conf.i_ai = NULL; conf.s_ad = NULL; conf.i_ad = NULL; conf.s_aa = NULL; conf.i_aa = NULL; conf.m_loc = 0; /* Maximum 0 means unlimited number of connections. */ conf.m_tcp = 0; /* Maximum 0 means unlimited number of connections. */ conf.uid = 0; conf.gid = 0; conf.suid = 0; conf.sgid = 0; conf.slbl = 5; conf.snbl = 5; conf.iglo = 0; conf.dglo = 0; conf.aglo = 0; conf.ptcp = 0; conf.pudp = 0; conf.linf = 0; conf.ldb = 0; $? "- config_init" } /** Allocate internal structures for configuration. @return 1 on success, 0 on error. */ static int config_allocate(void) { int back = 0; $? "+ config_allocate" conf.s_c = dk4sto_open(NULL); conf.s_p = dk4sto_open(NULL); conf.s_a = dk4sto_open(NULL); conf.s_ai = dk4sto_open(NULL); conf.s_ad = dk4sto_open(NULL); conf.s_aa = dk4sto_open(NULL); if ((NULL != conf.s_ai) && (NULL != conf.s_ad) && (NULL != conf.s_aa)) { if ((NULL != conf.s_c) && (NULL != conf.s_p) && (NULL != conf.s_a)) { dk4sto_set_comp(conf.s_c, class_compare, 0); dk4sto_set_comp(conf.s_p, printer_compare, 0); dk4sto_set_comp(conf.s_a, printer_alias_compare, 0); dk4sto_set_comp(conf.s_ai, dk4socket_allowed_peer_compare, 0); dk4sto_set_comp(conf.s_ad, dk4socket_allowed_peer_compare, 0); dk4sto_set_comp(conf.s_aa, dk4socket_allowed_peer_compare, 0); conf.i_c = dk4sto_it_open(conf.s_c, NULL); conf.i_p = dk4sto_it_open(conf.s_p, NULL); conf.i_a = dk4sto_it_open(conf.s_a, NULL); conf.i_ai = dk4sto_it_open(conf.s_ai, NULL); conf.i_ad = dk4sto_it_open(conf.s_ad, NULL); conf.i_aa = dk4sto_it_open(conf.s_aa, NULL); if ((NULL != conf.i_ai) && (NULL != conf.i_ad) && (NULL != conf.i_aa)) { if ((NULL != conf.i_c) && (NULL != conf.i_p) && (NULL != conf.i_a)) { back = 1; } #if TRACE_DEBUG else { $? "! allocation (4)" } #endif } #if TRACE_DEBUG else { $? "! allocation (3)" } #endif } #if TRACE_DEBUG else { $? "! allocation (2)" } #endif } #if TRACE_DEBUG else { $? "! allocation (1)" } #endif $? "- config_allocate %d", back return back; } /** Set backlog variable. @param iptr Address of backlog number variable. @param pv Text containing the number. @param fn File name for diagnostics. @param lineno Line number. @return 1 on success, 0 on error. */ static int set_backlog(int *iptr, char *pv, const char *fn, unsigned long lineno) { const char *ep = NULL; int i = 0; int back = 0; int res = 0; $? "+ set_backlog %s \"%s\"", TR_8PTR(iptr), TR_8STR(pv) res = dk4ma_input_c8_dec_int(&i, pv, &ep, 1, NULL); if (0 != res) { if (0 < i) { *iptr = i; back = 1; } else { $? "! not positive" /* ERROR: Must be positive */ log_1(fn, lineno, LOG_EMERG, 1, 15); } } else { $? "! not integer" /* ERROR: Not an integer number */ log_5(fn, lineno, LOG_EMERG, 1, 12, 13, 14, pv, ep); } $? "- set_backlog %d", back return back; } /** Set maximum number of connections. @param pmaxconn Address of result variable. @param pv Text containing the number. @param fn File name for diagnostics. @param lineno Line number. @return 1 on success, 0 on error. */ static int set_max_connections( size_t *pmaxconn, char *pv, const char *fn, unsigned long lineno ) { const char *ep = NULL; size_t sz = 0; int back = 0; int res = 0; $? "+ set_max_connections %s \"%s\"", TR_8PTR(pmaxconn), TR_8STR(pv) res = dk4ma_input_c8_dec_size_t(&sz, pv, &ep, 1, NULL); if (0 != res) { *pmaxconn = sz; back = 1; } else { $? "! not a size" /* ERROR: Not a size specification */ log_5(fn, lineno, LOG_EMERG, 1, 16, 17, 18, pv, ep); } $? "- set_max_connections %d", back return back; } /** Set port number from text. @param ppn Address of port number variable. @param pv Text containing the number. @return 1 on success, 0 on error. */ static int set_port_number( unsigned short *ppn, char *pv, const char *fn, unsigned long lineno ) { const char *ep = NULL; unsigned short pn = 0; int back = 0; int res = 0; $? "+ set_port_number %s \"%s\"", TR_8PTR(ppn), TR_8STR(pv) res = dk4ma_input_c8_dec_ushort(&pn, pv, &ep, 1, NULL); if (0 != res) { if (0 < pn) { *ppn = pn; back = 1; } else { $? "! 0 not allowed" /* ERROR: Port number must not be 0 */ log_1(fn, lineno, LOG_EMERG, 1, 22); } } else { $? "! not unsigned short" /* ERROR: Not a 16 bit unsigned number */ log_5(fn, lineno, LOG_EMERG, 1, 19, 20, 21, pv, ep); } $? "- set_port_number %d", back return back; } /** Add address to list of allowed hosts. @param s Containter of allowed peers. @param i Iterator through container. @param iptr Address of global flag variable. @param str Text containing the address. @return 1 on success, 0 on error. */ static int allow_hosts( dk4_sto_t *s, dk4_sto_it_t *i, int *iptr, char *str, const char *filename, unsigned long lineno ) { dk4_allowed_peer_t ap; dk4_allowed_peer_t *pap; char *px = NULL; int res = 0; int back = 0; $? "+ allow_hosts %s %s %s \"%s\"", TR_8PTR(s), TR_8PTR(i), TR_8PTR(iptr), TR_8STR(str) px = dk4str8_next(str, NULL); if (NULL == px) { if (0 == strcmp(str, printqd_kw[2])) { *iptr = 1; back = 1; } else { DK4_MEMRES(&ap, sizeof(ap)); res = dk4socket_c8_get_allowed_peer(&ap, str, NULL); if (DK4_SOCKET_RESULT_SUCCESS == res) { pap = dk4mem_new(dk4_allowed_peer_t,1,NULL); if (NULL != pap) { DK4_MEMCPY(pap, &ap, sizeof(ap)); if (0 != dk4sto_add(s, pap, NULL)) { back = 1; } else { $? "! storage add" dk4mem_free(pap); /* ERROR: Memory */ log_1(filename, lineno, LOG_EMERG, 1, 27); } } else { $? "! allocation" /* ERROR: Memory */ log_1(filename, lineno, LOG_EMERG, 1, 27); } } else { $? "! not a peer definition" /* ERROR: Syntax, not a peer definition */ log_3(filename, lineno, LOG_EMERG, 1, 25, 26, str); } } } else { $? "! unexpected text" /* ERROR: Unexpected text */ log_3(filename, lineno, LOG_EMERG, 1, 23, 24, px); } $? "- allow_hosts %d", back return back; } /** Obtain limit value from text. @param plim Address of limit variable to set. @param src Text containing the limit. @return 1 on success, 0 on error. */ static int get_limit_value(dk4_um_t *plim, char *src, const char *fn, unsigned long lineno) { const char *ep = NULL; dk4_um_t value = (dk4_um_t)0UL; int back = 0; int res = 0; $? "+ get_limit_value %s \"%s\"", TR_8PTR(plim), TR_8STR(src) if (0 == strcmp(src, printqd_kw[3])) { *plim = DK4_UM_MAX; back = 1; } else { if (0 == strcmp(src, printqd_kw[4])) { *plim = value; back = 1; } else { res = dk4ma_input_c8_dec_dk4_um_t(&value, src, &ep, 1, NULL); if (0 != res) { *plim = value; back = 1; } else { $? "! not an unsigned number" /* ERROR: Syntax, not an unsigned number */ log_5(fn, lineno, LOG_EMERG, 1, 28, 29, 30, src, ep); } } } $? "- get_limit_value %d", back return back; } /** Add a user or group limit to a class. @param pcl Class to modify. @param s Container for limits. @param i Container iterator. @param str Text containing name and limit. @param fn File name. @param lineno Line number. @return 1 on success, 0 on error. */ static int add_limit( pqd_class_t *pcl, dk4_sto_t *s, dk4_sto_it_t *i, char *str, const char *fn, dk4_um_t lineno ) { pqd_limit_t *plim; char *pnum; char *px; dk4_um_t limval = (dk4_um_t)0UL; int back = 0; $? "+ add_limit %s %s %s \"%s\"", TR_8PTR(pcl), TR_8PTR(s), TR_8PTR(i), TR_8STR(str) pnum = dk4str8_next(str, NULL); if (NULL != pnum) { px = dk4str8_next(pnum, NULL); if (NULL == px) { if (0 != get_limit_value(&limval, pnum, fn, lineno)) { plim = (pqd_limit_t *)dk4sto_it_find_like(i, str, 1); if (NULL == plim) { plim = limit_new(str, limval); if (NULL != plim) { if (0 != dk4sto_add(s, plim, NULL)) { back = 1; } else { $? "! allocation (2)" /* ERROR: Memory */ log_1(fn, lineno, LOG_EMERG, 1, 27); limit_delete(plim); } } else { $? "! allocation (1)" /* ERROR: Memory */ log_1(fn, lineno, LOG_EMERG, 1, 27); } } else { $? "! redefinition" /* ERROR: Limit redefinition */ log_3(fn, lineno, LOG_EMERG, 1, 32, 33, str); } } } else { $? "! unexpected text" /* ERROR: Syntax, no further text expected */ log_3(fn, lineno, LOG_EMERG, 1, 23, 24, px); } } else { $? "! no limit text" /* ERROR: Missing limit. */ log_1(fn, lineno, LOG_EMERG, 1, 31); } $? "- add_limit %d", back return back; } /** Set default limit for class. @param pcl Class to modify. @param pv String containing the limit. @return 1 on success, 0 on error. */ static int set_default_limit( pqd_class_t *pcl, char *pv, const char *fn, unsigned long lineno ) { char *px; dk4_um_t value = (dk4_um_t)0UL; int back = 0; $? "+ set_default_limit %s \"%s\"", TR_8PTR(pcl), TR_8STR(pv) px = dk4str8_next(pv, NULL); if (NULL == px) { if (0 != get_limit_value(&value, pv, fn, lineno)) { if ((0 == (1 & (pcl->hdl))) || (value == pcl->dl) ) { pcl->hdl |= 1; pcl->dl = value; back = 1; } else { $? "! redefinition" /* ERROR: Syntax, default limit redefined */ log_1(fn, lineno, LOG_EMERG, 1, 34); } } } else { $? "! unexpected text" /* ERROR: No further text expected */ log_3(fn, lineno, LOG_EMERG, 1, 23, 24, px); } $? "- set_default_limit %d", back return back; } /** Set deny action for printer class. @param pcl Class to modify. @param pv String containing the action. @return 1 on success, 0 on error. */ static int set_deny_action( pqd_class_t *pcl, char *pv, const char *fn, unsigned long lineno ) { char *px = NULL; int back = 0; int res = 0; $? "+ set_deny_action %s \"%s\"", TR_8PTR(pcl), TR_8STR(pv) px = dk4str8_next(pv, NULL); if (NULL == px) { switch ( (res = dk4str8_array_index(deny_action_names, pv, 0)) ) { case 0: case 1: { if (0 == (2 & pcl->hdl)) { pcl->da = res; pcl->hdl |= 2; back = 1; } else { $? "! redefinition" /* ERROR: Redefinition of deny action */ log_1(fn, lineno, LOG_EMERG, 1, 35); } } break; default: { } break; } } else { $? "! unexpected text" /* ERROR: Syntax, no further text expected */ log_3(fn, lineno, LOG_EMERG, 1, 23, 24, px); } $? "- set_deny_action %d", back return back; } /** Add an alias for the current printer to list. @param ppr Printer to add alias for. @param pv Alias name. @return 1 on success, 0 on error. */ static int add_alias(pqd_printer_t *ppr, char *pv, const char *fn, unsigned long lineno) { pqd_printer_alias_t *ppa; pqd_printer_t *pp; char *px; int back = 0; $? "+ add_alias %s \"%s\"", TR_8PTR(ppr), TR_8STR(pv) px = dk4str8_next(pv, NULL); if (NULL == px) { ppa = (pqd_printer_alias_t *)dk4sto_it_find_like(conf.i_a, pv, 1); if (NULL == ppa) { pp = (pqd_printer_t *)dk4sto_it_find_like(conf.i_p, pv, 1); if (NULL == pp) { ppa = printer_alias_new(pv, ppr); if (NULL != ppa) { if (0 != dk4sto_add(conf.s_a, ppa, NULL)) { back = 1; } else { $? "! allocation (2)" /* ERROR: Memory */ log_1(fn, lineno, LOG_EMERG, 1, 27); printer_alias_delete(ppa); } } else { $? "! allocation (1)" /* ERROR: Memory */ log_1(fn, lineno, LOG_EMERG, 1, 27); } } else { $? "! printer exists" /* ERROR: Printer exists */ log_3(fn, lineno, LOG_EMERG, 1, 36, 37, pv); } } else { $? "! alias exists" /* ERROR: Alias already exists */ log_3(fn, lineno, LOG_EMERG, 1, 38, 39, pv); } } else { $? "! unexpected text" /* ERROR: Syntax, unexpected text */ log_3(fn, lineno, LOG_EMERG, 1, 23, 24, px); } $? "- add_alias %d", back return back; } /** Assign a class to a printer. @param ppr Printer to modify. @param pv Class name. @return 1 on success, 0 on error. */ static int assign_class(pqd_printer_t *ppr, char *pv, const char *fn, unsigned long lineno) { pqd_class_t *pcl; char *px; int back = 0; $? "+ assign_class %s \"%s\"", TR_8PTR(ppr), TR_8STR(pv) px = dk4str8_next(pv, NULL); if (NULL == px) { if (NULL == ppr->cl) { pcl = (pqd_class_t *)dk4sto_it_find_like(conf.i_c, pv, 1); if (NULL != pcl) { ppr->cl = pcl; back = 1; } else { $? "! not found" /* ERROR: Syntax, class not found */ log_3(fn, lineno, LOG_EMERG, 1, 40, 41, pv); } } else { $? "! redefinition" /* ERROR: Syntax, redefinition of printer class */ log_1(fn, lineno, LOG_EMERG, 1, 42); } } else { $? "! unexpected text" /* ERROR: Syntax, unexpected text */ log_3(fn, lineno, LOG_EMERG, 1, 23, 24, px); } $? "- assign_class %d", back return back; } /** Check configuration for errors. @return 1 if configuration is usable, 0 on errors. */ static int config_check(const char *filename, unsigned long lineno) { pqd_printer_t *pr = NULL; pqd_class_t *cl = NULL; int back = 1; $? "+ config_check" /* Each printer must have a class assigned. */ dk4sto_it_reset(conf.i_p); do { pr = (pqd_printer_t *)dk4sto_it_next(conf.i_p); if (NULL != pr) { if (NULL == pr->cl) { back = 0; $? "! no class for printer \"%s\"", TR_8STR(pr->name) /* ERROR: No class for printer */ log_3(filename, lineno, LOG_EMERG, 1, 91, 92, pr->name); } } } while(NULL != pr); /* Each class should have a default limit */ dk4sto_it_reset(conf.i_c); do { cl = (pqd_class_t *)dk4sto_it_next(conf.i_c); if (NULL != cl) { if (0 == cl->hdl) { $? "! WARNING no default limit for class \"%s\"", TR_8STR(cl->name) /* WARNING: Default limit missing for class */ log_3(filename, lineno, LOG_EMERG, 0, 93, 94, cl->name); } } } while (NULL != cl); $? "- config_check %d", back return back; } /** Set additional features to log. @param pv Text containing the feature names. @param fn File name for diagnostics. @param lineno Line numbers for diagnostics. @return 1 on success, 0 on errors. */ static int set_log_features(char *pv, const char *fn, unsigned long lineno) { char *pc; char *pn; int back = 0; if (NULL != pv) { back = 1; pc = dk4str8_start(pv, NULL); while (NULL != pc) { pn = strchr(pc, ','); if (NULL != pn) { *(pn++) = '\0'; pn = dk4str8_start(pn, NULL); } dk4str8_normalize(pc, NULL); switch ( dk4str8_array_index(log_feature_names, pc, 0) ) { case 0 : { conf.ldb = 1; } break; case 1 : { conf.linf = 1; } break; case 2 : case 3 : { conf.ldb = 1; conf.linf = 1; } break; case 4 : case 5 : { conf.ldb = 0; conf.linf = 0; } break; default : { /* ERROR: Unknown log feature name */ log_3(NULL, 0UL, LOG_EMERG, 0, 154, 155, pc); back = 0; } break; } pc = pn; } } else { /* ERROR: Value required */ conf.ldb = 0; conf.linf = 0; } return back; } /** Read configuration file. @return 1 on success (can continue), 0 on error (must exit). */ static int config_read(void) { const char *fn = NULL; /* Input file name */ FILE *fipo = NULL; /* Input file */ pqd_class_t *pcl = NULL; /* Current class to modify */ pqd_printer_t *ppr = NULL; /* Current printer to modify */ pqd_printer_alias_t *ppa = NULL; /* Printer alias */ char *pk = NULL; /* Key text */ char *pv = NULL; /* Value text */ char *px = NULL; /* Text after value */ unsigned long lineno = 0UL; /* Current line number */ int back = 0; /* Result */ int sectt = 0; /* Section type: 0=opt, 1=class, 2=pr */ int myres = 0; /* Internal operation result */ $? "+ config_read" if (0 != config_allocate()) { if (0 != options[1].found) { fn = options[1].val.t; } if (NULL == fn) { fn = def_conf_file_name; } fipo = dk4fopen_c8(fn, printqd_kw[1], DK4_FOPEN_SC_IS_REGULAR, NULL); if (NULL != fipo) { back = 1; sectt = 0; while ((1 == back) && (NULL != fgets(inbuf, sizeof(inbuf), fipo))) { lineno++; dk4str8_delnl(inbuf); $? ". line = \"%s\"", inbuf pk = dk4str8_start(inbuf, NULL); if (NULL != pk) { if ('#' != *pk) { if ('[' == *pk) { $? ". section line" /* Opening a new section. */ pk = dk4str8_start(++pk, NULL); if (NULL != pk) { $? ". section \"%s\"", pk sectt = -1; pcl = NULL; ppr = NULL; pv = dk4str8_chr(pk, ']'); if (NULL != pv) { *pv = '\0'; } $? ". section = \"%s\"", pk pv = dk4str8_next(pk, NULL); switch ( dk4str8_array_index(config_section_names, pk, 0) ) { /* Opening options section. */ case 0 : { $? ". options section" sectt = 0; } break; /* Opening class section. */ case 1 : { $? ". class section" sectt = 1; if (NULL != pv) { if (NULL == (px = dk4str8_next(pv, NULL))) { pcl = (pqd_class_t *)dk4sto_it_find_like( conf.i_c, pv, 1 ); if (NULL == pcl) { pcl = class_new(pv); if (NULL != pcl) { if (0 == dk4sto_add(conf.s_c, pcl, NULL)) { /* ERROR: Memory */ log_1(fn, lineno, LOG_EMERG, 1, 27); class_delete(pcl); pcl = NULL; $? "! memory" back = 0; } } else { $? "! memory" /* ERROR: MEMORY */ log_1(fn, lineno, LOG_EMERG, 1, 27); back = 0; } } else { $? "! already exists" /* ERROR: Syntax, class already exists */ log_3(fn, lineno, LOG_EMERG, 1, 43, 44, pv); back = 0; } } else { $? "! unexpected text" /* ERROR: Syntax, only one component allowed */ log_3(fn, lineno, LOG_EMERG, 1, 23, 24, px); back = 0; } } else { $? "! missing class name" /* ERROR: Syntax, missing class name */ log_1(fn, lineno, LOG_EMERG, 1, 45); back = 0; } } break; /* Opening printer section. */ case 2 : { $? ". printer section" sectt = 2; if (NULL != pv) { if (NULL == (px = dk4str8_next(pv, NULL))) { ppr = (pqd_printer_t *)dk4sto_it_find_like( conf.i_p,pv,1 ); ppa = (pqd_printer_alias_t *)dk4sto_it_find_like( conf.i_a,pv,1 ); if ((NULL == ppr) && (NULL == ppa)) { ppr = printer_new(pv); if (NULL != ppr) { if (0 == dk4sto_add(conf.s_p, ppr, NULL)) { /* ERROR: Memory */ log_1(fn, lineno, LOG_EMERG, 1, 27); printer_delete(ppr); ppr = NULL; back = 0; $? "! memory" } } else { $? "! memory" /* ERROR: Memory */ log_1(fn, lineno, LOG_EMERG, 1, 27); back = 0; } } else { $? "! already exists" /* ERROR: Syntax, pr/al already exists */ log_3(fn, lineno, LOG_EMERG, 1, 46, 47, pv); back = 0; } } else { $? "! unexpected text" /* ERROR: Syntax, just one component allowed */ log_3(fn, lineno, LOG_EMERG, 1, 23, 24, px); back = 0; } } else { $? "! missing printer name" /* ERROR: Syntax, name required */ log_1(fn, lineno, LOG_EMERG, 1, 48); back = 0; } } break; default : { $? "! illegal section name" /* ERROR: Syntax, illegal section name */ log_3(fn, lineno, LOG_EMERG, 1, 49, 50, pk); back = 0; } break; } } else { $? "! empty section" /* ERROR: Syntax, empty section */ log_1(fn, lineno, LOG_EMERG, 1, 51); back = 0; } } else { $? ". data line" /* Data line for existing section. */ pv = dk4str8_chr(pk, '='); if (NULL != pv) { *(pv++) = '\0'; pv = dk4str8_start(pv, NULL); if (NULL != pv) { dk4str8_normalize(pk, NULL); $? ". pk=\"%s\" pv=\"%s\"",pk,pv switch ( dk4str8_array_index(config_option_names, pk, 0) ) { case 0 : { $? ". run as user" if (0 == sectt) { struct passwd *pw; if (NULL == (px = dk4str8_next(pv, NULL))) { pw = getpwnam(pv); if (NULL != pw) { if ((0 == conf.uid) || (conf.uid == pw->pw_uid)) { conf.uid = pw->pw_uid; } else { /* ERROR: Redefinition */ log_1(fn, lineno, LOG_EMERG, 1, 52); back = 0; $? "! redefinition" } } else { $? "! user not found" /* ERROR: User not found */ log_3(fn, lineno, LOG_EMERG, 1, 53, 54, pv); back = 0; } } else { $? "! unexpected text" /* ERROR: Syntax, just one component allowed */ log_3(fn, lineno, LOG_EMERG, 1, 23, 24, px); back = 0; } } else { $? "! only in options" /* ERROR: Only allwowed in options section */ log_1(fn, lineno, LOG_EMERG, 1, 55); back = 0; } } break; case 1 : { $? ". run as group" if (0 == sectt) { struct group *pgr; if (NULL == (px = dk4str8_next(pv, NULL))) { pgr = getgrnam(pv); if (NULL != pgr) { if ((0 == conf.gid) || (conf.gid == pgr->gr_gid)) { conf.gid = pgr->gr_gid; } else { /* ERROR: Syntax, group redefinition */ log_1(fn, lineno, LOG_EMERG, 1, 56); back = 0; $? "! redefinition" } } else { $? "! not found" /* ERROR: Syntax, group not found */ log_3(fn, lineno, LOG_EMERG, 1, 57, 58, pv); back = 0; } } else { $? "! unexpected text" /* ERROR: Syntax, just one component allowed */ log_3(fn, lineno, LOG_EMERG, 1, 23, 24, px); back = 0; } } else { $? "! only in options" /* ERROR: Syntax, only allowed in options */ log_1(fn, lineno, LOG_EMERG, 1, 55); back = 0; } } break; case 2 : { $? ". database" if (0 == sectt) { if (NULL == conf.dname) { conf.dname = dk4str8_dup(pv, NULL); if (NULL == conf.dname) { /* ERROR: Memory */ log_1(fn, lineno, LOG_EMERG, 1, 27); back = 0; $? "! memory" } } else { /* ERROR: Redefinition */ log_1(fn, lineno, LOG_EMERG, 1, 59); back = 0; $? "! redefinition" } } else { $? "! only in options" /* ERROR: Syntax, only allowed in options */ log_1(fn, lineno, LOG_EMERG, 1, 55); back = 0; } } break; case 3 : { $? ". local socket" if (0 == sectt) { if (NULL == conf.sname) { conf.sname = dk4str8_dup(pv, NULL); if (NULL == conf.sname) { /* ERROR: Memory */ log_1(fn, lineno, LOG_EMERG, 1, 27); back = 0; $? "! memory" } } else { $? "! redefinition" /* ERROR: Redefinition */ log_1(fn, lineno, LOG_EMERG, 1, 60); back = 0; } } else { $? "! only in options" /* ERROR: Syntax, only allowed in options */ log_1(fn, lineno, LOG_EMERG, 1, 55); back = 0; } } break; case 4 : { $? ". local socket backlog" if (0 == sectt) { if (0 == set_backlog(&(conf.slbl), pv, fn, lineno)) { back = 0; $? "! failed" } } else { $? "! only in options" /* ERROR: Syntax, only allowed in options */ log_1(fn, lineno, LOG_EMERG, 1, 55); back = 0; } } break; case 5 : { $? ". max local connections" if (0 == sectt) { if(0 == set_max_connections(&(conf.m_loc),pv,fn,lineno)) { back = 0; $? "! failed" } } else { $? "! only in options" /* ERROR: Syntax, only allowed in options */ log_1(fn, lineno, LOG_EMERG, 1, 55); back = 0; } } break; case 6 : { $? ". udp port" if (0 == sectt) { if (0 == set_port_number(&(conf.pudp),pv,fn,lineno)) { back = 0; $? "! failed" } } else { $? "! only in options" /* ERROR: Syntax, only allowed in options */ log_1(fn, lineno, LOG_EMERG, 1, 55); back = 0; } } break; case 7 : { $? ". tcp port" if (0 == sectt) { if (0 == set_port_number(&(conf.ptcp),pv,fn,lineno)) { back = 0; $? "! failed" } } else { $? "! only in options" /* ERROR: Syntax, only allowed in options */ log_1(fn, lineno, LOG_EMERG, 1, 55); back = 0; } } break; case 8 : { $? ". tcp port backlog" if (0 == sectt) { if (0 == set_backlog(&(conf.snbl), pv, fn, lineno)) { back = 0; $? "! failed" } } else { $? "! only in options" /* ERROR: Syntax, only allowed in options */ log_1(fn, lineno, LOG_EMERG, 1, 55); back = 0; } } break; case 9 : { $? ". max tcp connections" if (0 == sectt) { if(0 == set_max_connections(&(conf.m_tcp),pv,fn,lineno)) { back = 0; $? "! failed" } } else { $? "! only in options" /* ERROR: Syntax, only allowed in options */ log_1(fn, lineno, LOG_EMERG, 1, 55); back = 0; } } break; case 10 : { $? ". info allow" if (0 == sectt) { myres = allow_hosts( conf.s_ai, conf.i_ai, &(conf.iglo), pv, fn, lineno ); if (0 == myres) { $? "! failed" back = 0; } } else { $? "! only in options" /* ERROR: Syntax, only allowed in options */ log_1(fn, lineno, LOG_EMERG, 1, 55); back = 0; } } break; case 11 : { $? ". data allow" if (0 == sectt) { myres = allow_hosts( conf.s_ad, conf.i_ad, &(conf.dglo), pv, fn, lineno ); if (0 == myres) { $? "! failed" back = 0; } } else { $? "! only in options" /* ERROR: Syntax, only allowed in options */ log_1(fn, lineno, LOG_EMERG, 1, 55); back = 0; } } break; case 12 : { $? ". admin allow" if (0 == sectt) { myres = allow_hosts( conf.s_aa, conf.i_aa, &(conf.aglo), pv, fn, lineno ); if (0 == myres) { $? "! failed" back = 0; } } else { $? "! only in options" /* ERROR: Syntax, only allowed in options */ log_1(fn, lineno, LOG_EMERG, 1, 55); back = 0; } } break; case 13 : { $? ". user limit" if ((1 == sectt) && (NULL != pcl)) { myres = add_limit( pcl, pcl->s_u, pcl->i_u, pv, fn, lineno ); if (0 == myres) { $? "! failed" back = 0; } } else { $? "! only in class" /* ERROR: Syntax, only allowed in class */ log_1(fn, lineno, LOG_EMERG, 1, 61); back = 0; } } break; case 14 : { $? ". group limit" if ((1 == sectt) && (NULL != pcl)) { myres = add_limit( pcl, pcl->s_g, pcl->i_g, pv, fn, lineno ); if (0 == myres) { $? "! failed" back = 0; } } else { $? "! only in class" /* ERROR: Syntax, only allowed in class */ log_1(fn, lineno, LOG_EMERG, 1, 61); back = 0; } } break; case 15 : { $? ". default limit" if ((1 == sectt) && (NULL != pcl)) { myres = set_default_limit(pcl, pv, fn, lineno); if (0 == myres) { $? "! failed" back = 0; } } else { $? "! only in class" /* ERROR: Syntax, only allowed in class */ log_1(fn, lineno, LOG_EMERG, 1, 61); back = 0; } } break; case 16 : { $? ". deny action" if ((1 == sectt) && (NULL != pcl)) { myres = set_deny_action(pcl, pv, fn, lineno); if (0 == myres) { $? "! failed" back = 0; } } else { $? "! only in class" /* ERROR: Syntax, only allowed in class */ log_1(fn, lineno, LOG_EMERG, 1, 61); back = 0; } } break; case 17 : { $? ". alias" if ((2 == sectt) && (NULL != ppr)) { myres = add_alias(ppr, pv, fn, lineno); if (0 == myres) { $? "! failed" back = 0; } } else { $? "! only in printer" /* ERROR: Syntax, only allowed in printer */ log_1(fn, lineno, LOG_EMERG, 1, 62); back = 0; } } break; case 18 : { $? ". class" if ((2 == sectt) && (NULL != ppr)) { myres = assign_class(ppr, pv, fn, lineno); if (0 == myres) { $? "! failed" back = 0; } } else { $? "! only in printer" /* ERROR: Syntax, only allowed in printer */ log_1(fn, lineno, LOG_EMERG, 1, 62); back = 0; } } break; case 19 : { $? ". log file" if (0 == sectt) { if (NULL == conf.lname) { conf.lname = dk4str8_dup(pv, NULL); if (NULL == conf.lname) { /* ERROR: Memory */ log_1(fn, lineno, LOG_EMERG, 1, 27); back = 0; $? "! memory" } } else { $? "! redefinition" /* ERROR: Redefinition */ log_1(fn, lineno, LOG_EMERG, 1, 63); back = 0; } } else { $? "! only in options" /* ERROR: Only in options */ log_1(fn, lineno, LOG_EMERG, 1, 55); back = 0; } } break; case 20 : { $? ". log features" back = set_log_features(pv, fn, lineno); } break; case 21 : { $? ". local socket owner" if (0 == sectt) { struct passwd *pw; if (NULL == (px = dk4str8_next(pv, NULL))) { pw = getpwnam(pv); if (NULL != pw) { if ((0 == conf.suid) || (conf.suid == pw->pw_uid)) { conf.suid = pw->pw_uid; } else { /* ERROR: Redefinition */ log_1(fn, lineno, LOG_EMERG, 1, 52); back = 0; $? "! redefinition" } } else { $? "! user not found" /* ERROR: User not found */ log_3(fn, lineno, LOG_EMERG, 1, 53, 54, pv); back = 0; } } else { $? "! unexpected text" /* ERROR: Syntax, just one component allowed */ log_3(fn, lineno, LOG_EMERG, 1, 23, 24, px); back = 0; } } else { $? "! only in options" /* ERROR: Only allwowed in options section */ log_1(fn, lineno, LOG_EMERG, 1, 55); back = 0; } } break; case 22 : { $? ". local socket group" if (0 == sectt) { struct group *pgr; if (NULL == (px = dk4str8_next(pv, NULL))) { pgr = getgrnam(pv); if (NULL != pgr) { if((0 == conf.sgid) || (conf.sgid == pgr->gr_gid)) { conf.sgid = pgr->gr_gid; } else { /* ERROR: Syntax, group redefinition */ log_1(fn, lineno, LOG_EMERG, 1, 56); back = 0; $? "! redefinition" } } else { $? "! not found" /* ERROR: Syntax, group not found */ log_3(fn, lineno, LOG_EMERG, 1, 57, 58, pv); back = 0; } } else { $? "! unexpected text" /* ERROR: Syntax, just one component allowed */ log_3(fn, lineno, LOG_EMERG, 1, 23, 24, px); back = 0; } } else { $? "! only in options" /* ERROR: Syntax, only allowed in options */ log_1(fn, lineno, LOG_EMERG, 1, 55); back = 0; } } break; } } else { $? "! value text missing" /* ERROR: Syntax, missing value */ log_1(fn, lineno, LOG_EMERG, 1, 64); back = 0; } } else { $? "! value text missing" /* ERROR: Syntax, missing value */ log_1(fn, lineno, LOG_EMERG, 1, 64); back = 0; } } } #if TRACE_DEBUG else { $? ". comment line" } #endif } #if TRACE_DEBUG else { $? ". empty line" } #endif } fclose(fipo); if (1 == back) { back = config_check(fn, lineno); } } else { $? "! failed to open file" /* ERROR: Failed to open file */ log_1(fn, 0UL, LOG_EMERG, 1, 65); } } else { /* ERROR: Memory */ log_1(NULL, 0UL, LOG_EMERG, 1, 27); } $? "- config_read %d", back return back; } static void release_allowed_peers(dk4_sto_it_t *it) { dk4_allowed_peer_t *peer; $? "+ release_allowed_peers %s", TR_8PTR(it) dk4sto_it_reset(it); do { peer = (dk4_allowed_peer_t *)dk4sto_it_next(it); if (NULL != peer) { dk4mem_free(peer); } } while (NULL != peer); dk4sto_it_close(it); $? "- release_allowed_peers" } /** Release resources used to save configuration data. */ static void config_release(void) { $? "+ config_release" prev_uid = conf.uid; prev_gid = conf.gid; if (NULL != conf.i_aa) { /* Allowed peers for administration */ release_allowed_peers(conf.i_aa); } conf.i_aa = NULL; if (NULL != conf.s_aa) { dk4sto_close(conf.s_aa); } conf.s_aa = NULL; if (NULL != conf.i_ad) { /* Allowed peers for data */ release_allowed_peers(conf.i_ad); } conf.i_ad = NULL; if (NULL != conf.s_ad) { dk4sto_close(conf.s_ad); } conf.s_ad = NULL; if (NULL != conf.i_ai) { /* Allowed peers for information */ release_allowed_peers(conf.i_ai); } conf.i_ai = NULL; if (NULL != conf.s_ai) { dk4sto_close(conf.s_ai); } conf.s_ai = NULL; if (NULL != conf.i_a) { /* Aliases */ printer_alias_all_delete(conf.i_a); } conf.i_a = NULL; if (NULL != conf.s_a) { dk4sto_close(conf.s_a); } conf.s_a = NULL; if (NULL != conf.i_p) { /* Printers */ printer_all_delete(conf.i_p); } conf.i_p = NULL; if (NULL != conf.s_p) { dk4sto_close(conf.s_p); } conf.s_p = NULL; if (NULL != conf.i_c) { /* Classes */ class_all_delete(conf.i_c); } conf.i_c = NULL; if (NULL != conf.s_c) { dk4sto_close(conf.s_c); } conf.s_c = NULL; dk4mem_release(conf.dname); dk4mem_release(conf.sname); dk4mem_release(conf.lname); conf.m_loc = 0; conf.m_tcp = 0; conf.uid = 0; conf.gid = 0; conf.slbl = 0; conf.snbl = 0; conf.ptcp = 9100; conf.pudp = 9100; $? "- config_release" } /** Allocate resources before starting outer loop. @return 1 on success (can enter loop), 0 on error (exit). */ static int outer_allocate_resources(void) { int back = 0; $? "+ outer_allocate_resources" /* Initialize variables to NULL. */ s_c_unix = NULL; i_c_unix = NULL; s_c_tcp = NULL; i_c_tcp = NULL; /* Allocate resources. */ s_c_unix = dk4sto_open(NULL); if (NULL == s_c_unix) { $? "! allocation (1)" /* ERROR: Memory */ log_1(NULL, 0UL, LOG_EMERG, 1, 27); goto finished; } dk4sto_set_comp(s_c_unix, compare_pqd_l_conn_t, 0); i_c_unix = dk4sto_it_open(s_c_unix, NULL); if (NULL == i_c_unix) { $? "! allocation (2)" /* ERROR: Memory */ log_1(NULL, 0UL, LOG_EMERG, 1, 27); goto finished; } s_c_tcp = dk4sto_open(NULL); if (NULL == s_c_tcp) { $? "! allocation (3)" /* ERROR: Memory */ log_1(NULL, 0UL, LOG_EMERG, 1, 27); goto finished; } dk4sto_set_comp(s_c_tcp, compare_pqd_n_conn_t, 0); i_c_tcp = dk4sto_it_open(s_c_tcp, NULL); if (NULL == i_c_tcp) { $? "! allocation (4)" /* ERROR: Memory */ log_1(NULL, 0UL, LOG_EMERG, 1, 27); goto finished; } /* Indicate success. */ back = 1; finished: $? "- outer_allocate_resources %d", back return back; } /** Release resources after finishing outer loop. */ static void outer_release_resources(void) { pqd_n_conn_t *pnc; pqd_l_conn_t *plc; $? "+ outer_release_resources" if (NULL != i_c_tcp) { dk4sto_it_reset(i_c_tcp); do { pnc = (pqd_n_conn_t *)dk4sto_it_next(i_c_tcp); if (NULL != pnc) { /* Release pnc */ if (INVALID_SOCKET != pnc->sock) { (void)dk4socket_close(pnc->sock, NULL); } dk4mem_free(pnc); } } while(NULL != pnc); dk4sto_it_close(i_c_tcp); } i_c_tcp = NULL; if (NULL != s_c_tcp) { dk4sto_close(s_c_tcp); } s_c_tcp = NULL; if (NULL != i_c_unix) { dk4sto_it_reset(i_c_unix); do { plc = (pqd_l_conn_t *)dk4sto_it_next(i_c_unix); if (NULL != plc) { /* Release plc */ if (INVALID_SOCKET != plc->sock) { (void)dk4socket_close(plc->sock, NULL); } dk4mem_free(plc); } } while (NULL != plc); dk4sto_it_close(i_c_unix); } i_c_unix = NULL; if (NULL != s_c_unix) { dk4sto_close(s_c_unix); } s_c_unix = NULL; $? "- outer_release_resources" } static void pqd_dbi_open_error(const char *fn, dk4_er_t *erp) { switch (erp->ec) { case DK4_E_NOT_SUPPORTED : { log_3(NULL, 0UL, LOG_EMERG, 1, 105, 202, fn); } break; case DK4_E_MATH_OVERFLOW : { log_3(NULL, 0UL, LOG_EMERG, 1, 105, 203, fn); } break; case DK4_E_MEMORY_ALLOCATION_FAILED : { log_3(NULL, 0UL, LOG_EMERG, 1, 105, 204, fn); } break; case DK4_E_SYNTAX : { log_3(NULL, 0UL, LOG_EMERG, 1, 105, 205, fn); } break; case DK4_E_SEC_CHECK : { log_3(NULL, 0UL, LOG_EMERG, 1, 105, 206, fn); } break; case DK4_E_OPEN_READ_FAILED : { log_3(NULL, 0UL, LOG_EMERG, 1, 105, 207, fn); } break; case DK4_E_OPEN_WRITE_FAILED : { log_3(NULL, 0UL, LOG_EMERG, 1, 105, 208, fn); } break; case DK4_E_CLOSE_FAILED : { log_3(NULL, 0UL, LOG_EMERG, 1, 105, 209, fn); } break; default : { log_3(NULL, 0UL, LOG_EMERG, 1, 105, 106, fn); } break; } } /** Allocate resources before starting inner loop. * Create log directory and log file. * Open database. * Log message for starting service. @return 1 on success (can enter loop), 0 on error (exit). */ static int inner_allocate_resources(void) { char necbuf[16*sizeof(dk4_um_t)]; dk4_er_t er; const char *fn; uid_t sockuid; gid_t sockgid; int res; int back = 0; $? "+ inner_allocate_resources" /* Log file directory hierarchy. */ fn = conf.lname; if (NULL == fn) { fn = def_log_file_name; } if (0 != user_switched) { res = dk4mkdir_hierarchy_c8(fn, 0, NULL); } else { res = dk4mkdir_hierarchy_ugm_c8(fn, 0, conf.uid, conf.gid, 0750, NULL); } if (0 == res) { $? "! log dir hierarchy" /* ERROR: Failed to create directory hierachy for log file */ log_3(NULL, 0UL, LOG_EMERG, 1, 95, 96, fn); goto finished; } /* Database directory hierarchy */ fn = conf.dname; if (NULL == fn) { fn = def_db_file_name; } fn = dk4dbi_filename_part(fn); if (0 != user_switched) { res = dk4mkdir_hierarchy_c8(fn, 0, NULL); } else { res = dk4mkdir_hierarchy_ugm_c8(fn, 0, conf.uid, conf.gid, 0750, NULL); } if (0 == res) { $? "! db dir hierarchy" /* ERROR: Failed to create directory hierarchy for db file */ log_3(NULL, 0UL, LOG_EMERG, 1, 97, 98, fn); goto finished; } /* Socket directory hierarchy */ fn = conf.sname; if (NULL == fn) { fn = def_sock_file_name; } sockuid = conf.suid; if (0 == sockuid) { sockuid = conf.uid; } sockgid = conf.sgid; if (0 == sockgid) { sockgid = conf.gid; } if ((0 != user_switched) && (0 != conf.uid)) { res = dk4mkdir_hierarchy_c8(fn, 0, NULL); } else { /* In contrast to the db and log directories the non-privileged user needs write access to the directory as the socket is deleted and newly created. */ res = dk4mkdir_hierarchy_ugm_c8(fn, 0, sockuid, sockgid, 0770, NULL); } if (0 == res) { $? "! socket dir hierarchy" /* ERROR: Failed to create directory hierarchy for socket */ log_3(NULL, 0UL, LOG_EMERG, 1, 99, 100, fn); goto finished; } /* UNIX listener socket. */ fn = conf.sname; if (NULL == fn) { fn = def_sock_file_name; } $? ". create local socket \"%s\"", fn dk4error_init(&er); if ( ((0 != sockuid) || (0 != sockgid)) && ((0 == user_switched) || (0 == conf.uid)) ) { so_unix = dk4socket_c8_unix_listener_owner_group_mode( fn, ((0 < conf.slbl) ? (conf.slbl) : 5), sockuid, sockgid, 0660, &er ); } else { so_unix = dk4socket_c8_unix_listener( fn, ((0 < conf.slbl) ? (conf.slbl) : 5), &er ); } if (INVALID_SOCKET == so_unix) { $? "! local socket" /* ERROR: Failed to create listener socket */ res = dk4ma_write_c8_decimal_signed( necbuf, sizeof(necbuf), (dk4_im_t)(er.dt.iDetails1), 0, NULL ); if (0 != res) { switch (er.ec) { case DK4_E_SYNTAX : { log_3(NULL, 0UL, LOG_EMERG, 1, 101, 178, fn); } break; case DK4_E_NON_SOCKET : { log_3(NULL, 0UL, LOG_EMERG, 1, 101, 179, fn); } break; case DK4_E_UNLINK_FAILED : { log_3(NULL, 0UL, LOG_EMERG, 1, 101, 180, fn); } break; case DK4_E_SOCKET_SOCKET : { log_5(NULL, 0UL, LOG_EMERG, 1, 101, 181, 182, fn, necbuf); } break; case DK4_E_SOCKET_BIND : { log_5(NULL, 0UL, LOG_EMERG, 1, 101, 183, 182, fn, necbuf); } break; case DK4_E_SOCKET_LISTEN : { log_5(NULL, 0UL, LOG_EMERG, 1, 101, 184, 182, fn, necbuf); } break; case DK4_E_CHOWN_FAILED : { log_3(NULL, 0UL, LOG_EMERG, 1, 101, 185, fn); } break; case DK4_E_CHMOD_FAILED : { log_3(NULL, 0UL, LOG_EMERG, 1, 101, 186, fn); } break; default : { log_3(NULL, 0UL, LOG_EMERG, 1, 101, 102, fn); } break; } } else { log_3(NULL, 0UL, LOG_EMERG, 1, 101, 102, fn); } goto finished; } $? ". local socket" /* TCP listener socket set. */ if (0 < conf.ptcp) { dk4error_init(&er); ss_tcp = dk4socket_c8_set_server( conf.ptcp, ((0 < conf.snbl) ? (conf.snbl) : 5), 0, &er ); if (NULL == ss_tcp) { $? "! tcp socket set" /* ERROR: Failed to create TCP listener socket */ res = dk4ma_write_c8_decimal_signed( necbuf, sizeof(necbuf), (dk4_im_t)(er.dt.iDetails1), 0, NULL ); if (0 != res) { switch (er.ec) { case DK4_E_MATH_OVERFLOW : { log_1(NULL, 0UL, LOG_EMERG, 1, 188); } break; case DK4_E_MEMORY_ALLOCATION_FAILED : { log_1(NULL, 0UL, LOG_EMERG, 1, 189); } break; case DK4_E_SOCKET_SOCKET : { log_3(NULL, 0UL, LOG_EMERG, 1, 190, 191, necbuf); } break; case DK4_E_SOCKET_BIND : { log_3(NULL, 0UL, LOG_EMERG, 1, 192, 191, necbuf); } break; case DK4_E_SOCKET_LISTEN : { log_3(NULL, 0UL, LOG_EMERG, 1, 193, 191, necbuf); } break; case DK4_E_SOCKET_GETADDRLOC : { log_3(NULL, 0UL, LOG_EMERG, 1, 194, 191, necbuf); } break; default : { log_1(NULL, 0UL, LOG_EMERG, 1, 103); } break; } } else { log_1(NULL, 0UL, LOG_EMERG, 1, 103); } goto finished; } } /* UDP socket set. */ if (0 < conf.pudp) { dk4error_init(&er); ss_udp = dk4socket_c8_set_udp(conf.pudp, 0, 0, &er); if (NULL == ss_udp) { $? "! udp socket set" /* ERROR: Failed to create UDP socket set */ res = dk4ma_write_c8_decimal_signed( necbuf, sizeof(necbuf), (dk4_im_t)(er.dt.iDetails1), 0, NULL ); if (0 != res) { switch (er.ec) { case DK4_E_MATH_OVERFLOW : { log_1(NULL, 0UL, LOG_EMERG, 1, 195); } break; case DK4_E_MEMORY_ALLOCATION_FAILED : { log_1(NULL, 0UL, LOG_EMERG, 1, 196); } break; case DK4_E_SOCKET_SOCKET : { log_3(NULL, 0UL, LOG_EMERG, 1, 197, 198, necbuf); } break; case DK4_E_SOCKET_BIND : { log_3(NULL, 0UL, LOG_EMERG, 1, 199, 198, necbuf); } break; case DK4_E_SOCKET_LISTEN : { log_3(NULL, 0UL, LOG_EMERG, 1, 200, 198, necbuf); } break; case DK4_E_SOCKET_GETADDRLOC : { log_3(NULL, 0UL, LOG_EMERG, 1, 201, 198, necbuf); } break; } } else { log_1(NULL, 0UL, LOG_EMERG, 1, 104); } goto finished; } } fn = conf.dname; if (NULL == fn) { fn = def_db_file_name; } $? ". db open attempt 1 \"%s\"", fn dk4error_init(&er); db = dk4dbi_open(fn, 1, 0, 1024, 1024, &er); if (NULL != db) { $? ". open db (1) ok" db_modified = 0; res = 1; dk4error_init(&er); if (0 == dbi_set(db, printqd_kw[79],printqd_kw[79],&er)) { res = 0; } if (0 == dk4dbi_close(db, &er)) { res = 0; } db = NULL; db_modified = 0; if (0 != res) { $? ". db open attempt 2 \"%s\"", fn dk4error_init(&er); db = dk4dbi_open(fn, 1, 0, 1024, 1024, &er); if (NULL != db) { $? ". open db (2) ok" db_modified = 0; } else { $? "! open db (2)" /* ERROR: Failed to open database */ pqd_dbi_open_error(fn, &er); goto finished; } } else { $? "! initial db entry" /* ERROR: Failed to write initial entry to database */ log_1(NULL, 0UL, LOG_EMERG, 1, 107); switch (er.ec) { case DK4_E_SYNTAX : { log_1(NULL, 0UL, LOG_EMERG, 1, 210); } break; case DK4_E_NOT_SUPPORTED : { log_1(NULL, 0UL, LOG_EMERG, 1, 211); } break; case DK4_E_MEMORY_ALLOCATION_FAILED : { log_1(NULL, 0UL, LOG_EMERG, 1, 212); } break; case DK4_E_OPEN_WRITE_FAILED : { log_1(NULL, 0UL, LOG_EMERG, 1, 213); } break; case DK4_E_WRITE_FAILED : { log_1(NULL, 0UL, LOG_EMERG, 1, 214); } break; case DK4_E_CLOSE_FAILED : { log_1(NULL, 0UL, LOG_EMERG, 1, 215); } break; case DK4_E_SEC_CHECK : { log_1(NULL, 0UL, LOG_EMERG, 1, 216); } break; default : { log_1(NULL, 0UL, LOG_EMERG, 1, 107); } break; } goto finished; } } else { $? "! open db (1)" /* ERROR: Failed to open database */ pqd_dbi_open_error(fn, &er); goto finished; } /* Finally indicate success. */ back = 1; finished: $? "- inner_allocate_resources %d", back return back; } /** Release resources after finishing inner loop. * Log message for finishing service. * Synchronize and close database. * Close log file. */ static int inner_release_resources(void) { const char *fn; int back = 1; $? "+ inner_release_resources" /* Database */ if (NULL != db) { if (0 == dk4dbi_close(db, NULL)) { $? "! sync db" /* ERROR: Failed to synchronize database to disk */ log_1(NULL, 0UL, LOG_EMERG, 1, 108); back = 0; } db = NULL; db_modified = 0; } /* UDP socket set. */ if (NULL != ss_udp) { $? "! udp socket set close" dk4socket_set_close(ss_udp, NULL); } ss_udp = NULL; /* TCP listener socket set. */ if (NULL != ss_tcp) { $? "! tcp socket set close" dk4socket_set_close(ss_tcp, NULL); } ss_tcp = NULL; /* Unix listener socket. */ if (INVALID_SOCKET != so_unix) { dk4socket_close(so_unix, NULL); } so_unix = INVALID_SOCKET; fn = conf.sname; if (NULL == fn) { fn = def_sock_file_name; } (void)dk4delete_file_c8(fn, NULL); $? "- inner_release_resources" return back; } /** Switch to non-privileged user and group if configured to do so. @return 1 on success (can continue), 0 on error (exit). */ static int change_group_and_user(void) { const char *fn = NULL; uid_t myuid; gid_t mygid; int dbt = DK4_DB_BE_UNKNOWN; int back = 1; $? "+ change_group_and_user" if (0 != user_switched) { $? ". already switched" if (conf.uid != prev_uid) { back = 0; $? "! re-switch user" /* ERROR: User to run as was changed */ log_1(NULL, 0UL, LOG_EMERG, 1, 66); } else { if (conf.gid != prev_gid) { $? "! re-switch group" back = 0; /* ERROR: Group to run as was changed */ log_1(NULL, 0UL, LOG_EMERG, 1, 67); } } } else { $? ". not yet switched" user_switched = 1; myuid = geteuid(); mygid = getegid(); if ((myuid != conf.uid) || (mygid != conf.gid)) { $? ". must switch" /* PID file ownership and permissions */ if (0 != chown(pid_file_name, conf.uid, conf.gid)) { back = 0; /* ERROR: Failed to change PID file ownership */ log_1(NULL, 0UL, LOG_EMERG, 1, 217); } if (0 != chmod(pid_file_name, 0660)) { back = 0; /* ERROR: Failed to change PID file permissions */ log_1(NULL, 0UL, LOG_EMERG, 1, 218); } /* Log file ownership and permissions */ fn = conf.lname; if (NULL == fn) { fn = def_log_file_name; } if (0 != chown(fn, conf.uid, conf.gid)) { back = 0; $? "! log file ownership" /* ERROR: Failed to change log file ownership */ log_1(NULL, 0UL, LOG_EMERG, 1, 109); } if (0 != chmod(fn, 0660)) { back = 0; $? "! log file permission" /* ERROR: Failed to change log file permissions */ log_1(NULL, 0UL, LOG_EMERG, 1, 110); } /* Database ownership and permissions */ if (0 == dk4dbi_change_owner_and_mode(db,conf.uid,conf.gid,0660,NULL)) { back = 0; $? "! db ownership and permissions" /* ERROR: failed to change db ownership and permissions */ log_1(NULL, 0UL, LOG_EMERG, 1, 111); } switch ( (dbt = dk4dbi_get_type(db)) ) { case DK4_DB_BE_BDB : case DK4_DB_BE_NDBM : { $? ". close database, must re-open after switching user" if (0 == dk4dbi_close(db, NULL)) { $? "! close failed" /* ERROR: Failed to synchronize database to disk */ log_1(NULL, 0UL, LOG_EMERG, 1, 121); } db = NULL; db_modified = 0; if (DK4_DB_BE_BDB == dbt) { fn = ((NULL != conf.dname) ? (conf.dname) : (def_db_file_name)); fn = dk4dbi_filename_part(fn); if (0 != chown(fn, conf.uid, conf.gid)) { back = 0; log_3(NULL, 0UL, LOG_EMERG, 1, 174, 175, fn); } if (0 != chmod(fn, 0660)) { back = 0; log_3(NULL, 0UL, LOG_EMERG, 1, 176, 177, fn); } } } break; } /* Local UNIX socket ownership and permission */ if (INVALID_SOCKET != so_unix) { $? ". socket open" $? ". fchown socket" if (0 != fchown(so_unix, conf.uid, conf.gid)) { back = 0; $? "! socket ownership" /* ERROR: Failed to change socket ownership */ log_1(NULL, 0UL, LOG_EMERG, 1, 112); } $? ". fchmod socket" if (0 != fchmod(so_unix, 0660)) { back = 0; $? "! socket permissions" /* ERROR: Failed to change socket permissions */ log_1(NULL, 0UL, LOG_EMERG, 1, 113); } } else { $? "! socket not yet open" } /* Switch group */ if (0 != setgid(conf.gid)) { back = 0; $? "! switch group" /* ERROR: Failed to switch group */ log_1(NULL, 0UL, LOG_EMERG, 1, 114); } /* Switch user */ if (0 != setuid(conf.uid)) { back = 0; $? "! switch user" /* ERROR: Failed to switch user */ log_1(NULL, 0UL, LOG_EMERG, 1, 115); } /* Re-open database if closed. */ if (NULL == db) { $? ". must reopen database" fn = conf.dname; if (NULL == fn) { fn = def_db_file_name; } db = dk4dbi_open(fn, 1, 0, 1024, 1024, NULL); if (NULL != db) { db_modified = 0; } else { $? "! failed to re-open database" back = 0; /* ERROR: Failed to open database */ log_3(NULL, 0UL, LOG_EMERG, 1, 105, 106, fn); } } } else { $? ". user/group already ok" } } $? "- change_group_and_user %d", back return back; } static int protocol_level_for_address(struct sockaddr *so) { int back = PQD_PROTO_NONE; $? "+ protocol_level_for_address" if (0 != conf.aglo) { back = PQD_PROTO_ADMIN; } else { if (NULL != dk4sto_it_find_like(conf.i_aa, so, 1)) { back = PQD_PROTO_ADMIN; } else { if (0 != conf.dglo) { back = PQD_PROTO_DATA; } else { if (NULL != dk4sto_it_find_like(conf.i_ad, so, 1)) { back = PQD_PROTO_DATA; } else { if (0 != conf.iglo) { back = PQD_PROTO_INFO; } else { if (NULL != dk4sto_it_find_like(conf.i_ai, so, 1)) { back = PQD_PROTO_INFO; } } } } } } $? "- protocol_level_for_address %d", back return back; } /** Check whether the action is allowed in the protocol level. @param act Action to check. @param protlev Protocol level. @return 1 if action is allowed, 0 otherwise. */ static int protocol_level_check(int act, int protlev) { int back = 0; $? "+ protocol_level_check act=%d protlev=%d", act, protlev switch (act) { case PQD_CMD_INFO : { if (PQD_PROTO_INFO <= protlev) { back = 1; } } break; case PQD_CMD_CONTROL : { if (PQD_PROTO_ADMIN <= protlev) { back = 1; } } break; default : { if (PQD_PROTO_DATA <= protlev) { back = 1; } } break; } $? "- protocol_level_check %d", back return back; } static void job_init(pqd_rq_t *ptr) { $? "+ job_init %s", TR_8PTR(ptr) DK4_MEMRES(ptr, sizeof(pqd_rq_t)); ptr->connptr = NULL; ptr->sto = NULL; ptr->soa = NULL; ptr->pnconn = NULL; ptr->cmdptr = NULL; ptr->argptr = NULL; ptr->szsoa = 0; ptr->sock = 0; ptr->action = 0; ptr->protlev = PQD_PROTO_NONE; ptr->logthis = 1; $? "- job_init" } static void account_init(pqd_account_t *ptr) { $? "+ account_init %s", TR_8PTR(ptr) DK4_MEMRES(ptr, sizeof(pqd_account_t)); ptr->limit = (dk4_um_t)0UL; ptr->used = (dk4_um_t)0UL; ptr->account = (dk4_um_t)0UL; ptr->da = 0; $? "- account_init" } /** Close a stream connection record. @param sock Socket to close. @param sto Container for the records. @param ptr Record to delete, containing the socket. @param pnconn Address of connection number variable to decrease. */ static void close_stream_conn( dk4_socket_t sock, dk4_sto_t *sto, void *ptr, size_t *pnconn ) { $? "+ close_stream_conn %d", (int)sock if ((NULL != sto) && (NULL != ptr)) { dk4sto_remove(sto, ptr, NULL); } if (INVALID_SOCKET != sock) { (void)dk4socket_close(sock, NULL); } if (NULL != ptr) { dk4mem_free(ptr); } if (NULL != pnconn) { if (0 < *pnconn) { *pnconn -= 1; } } $? "- close_stream_conn" } /** Send a text response. @param job Job structure. @param str Text to send (still without newline). */ static void send_text(pqd_rq_t *job, const char *str) { const char * msgs[3]; /* Log message text parts */ size_t sz; /* Number of bytes to send */ size_t wr; /* Number of bytes successfully sent */ int res; /* Operation result */ $? "+ send_text \"%s\"", TR_8STR(str) if (0 != dk4str8_cpy_s(inbuf, sizeof(inbuf), str, NULL)) { if (0 != job->logthis) { msgs[0] = printqd_kw[86]; msgs[1] = str; msgs[2] = NULL; log_multipart_message(NULL, 0UL, LOG_EMERG, 0, msgs, 2); } if (0 != dk4str8_cat_s(inbuf, sizeof(inbuf), printqd_kw[5], NULL)) { sz = dk4str8_len(inbuf); wr = sz; if ((NULL != job->soa) && (0 < job->szsoa)) { $? ". UDP" (void)dk4socket_sendto( job->sock, inbuf, &wr, 0, job->soa, job->szsoa, 0L, 0L, NULL ); } else { $? ". TCP or local" res = dk4socket_send(job->sock, inbuf, &wr, 0, 0L, 0L, NULL); switch (res) { case DK4_SOCKET_RESULT_SUCCESS : case DK4_SOCKET_RESULT_IN_PROGRESS : { if (wr != sz) { $? "! partial send" res = DK4_SOCKET_RESULT_FAILED; } } break; } if (DK4_SOCKET_RESULT_FAILED == res) { $? "! partial or no send" close_stream_conn(job->sock, job->sto, job->connptr, job->pnconn); } } } else { $? "! text too long (2)" /* ERROR: Response text too long (BUG)! */ log_1(NULL, 0UL, LOG_EMERG, 1, 116); } } else { $? "! text too long (1)" /* ERROR: Response text too long (BUG)! */ log_1(NULL, 0UL, LOG_EMERG, 1, 116); } $? "- send_text" } /** Send response for info or acct-check request. @param job Job structure. @param account Account structure containing limits and used pages. @param summary Summary: Allowed to print. @param how How to respond: 0=info, 1=acct-check/jobstart. */ static void send_response( pqd_rq_t *job, pqd_account_t *account, int summary, int how ) { char res[16+32*sizeof(dk4_um_t)]; char b1[16+8*sizeof(dk4_um_t)]; int ok = 0; $? "+ send_response" switch (how) { case 0: { /* Limit Used Account Summary */ if (DK4_UM_MAX == account->limit) { ok = dk4str8_cpy_s(res, sizeof(res), printqd_kw[84], NULL); } else { ok = dk4ma_write_c8_decimal_unsigned( b1, sizeof(b1), account->limit, 0, NULL ); if (0 != ok) { ok = dk4str8_cpy_s(res, sizeof(res), b1, NULL); } } if (0 != ok) { ok = dk4ma_write_c8_decimal_unsigned( b1, sizeof(b1), account->used, 0, NULL ); if (0 != ok) { ok = dk4str8_cat_s(res, sizeof(res), printqd_kw[85], NULL); if (0 != ok) { ok = dk4str8_cat_s(res, sizeof(res), b1, NULL); if (0 != ok) { ok = dk4ma_write_c8_decimal_unsigned( b1, sizeof(b1), account->account, 0, NULL ); if (0 != ok) { ok = dk4str8_cat_s(res, sizeof(res), printqd_kw[85], NULL); if (0 != ok) { ok = dk4str8_cat_s(res, sizeof(res), b1, NULL); if (0 != ok) { ok = dk4ma_write_c8_decimal_signed( b1, sizeof(b1), (dk4_im_t)summary, 0, NULL ); if (0 != ok) { ok = dk4str8_cat_s(res,sizeof(res),printqd_kw[85],NULL); if (0 != ok) { ok = dk4str8_cat_s(res, sizeof(res), b1, NULL); } } } } } } } } } if (0 != ok) { send_text(job, res); } else { send_text(job, printqd_kw[83]); } } break; default: { if (0 != summary) { send_text(job, printqd_kw[80]); } else { switch (account->da) { case 0: { send_text(job, printqd_kw[81]); } break; default: { send_text(job, printqd_kw[82]); } break; } } } break; } $? "- send_response" } /** Get printer entry for name (printer or alias name). @param name Printer or alias name. @return Printer entry. */ static pqd_printer_t * get_printer_for_name(const char *name) { pqd_printer_t *back = NULL; pqd_printer_alias_t *al = NULL; $? "+ get_printer_for_name \"%s\"", TR_8STR(name) back = (pqd_printer_t *)dk4sto_it_find_like(conf.i_p, name, 1); if (NULL == back) { al = (pqd_printer_alias_t *)dk4sto_it_find_like(conf.i_a, name, 1); if (NULL != al) { back = al->pr; } } $? "- get_printer_for_name %s", TR_8PTR(back) return back; } /** Save one dk4_um_t value to database. @param kwi Keyword index for key start. @param un User name. @param cn Class name. @param val Value to store. @return 1 on success, 0 on error. */ static int save_one_number( size_t kwi, const char *un, const char *cn, dk4_um_t val ) { int back = 0; int strok = 0; $? "+ save_one_number \"%s\" \"%s\" %lu", TR_8STR(un), TR_8STR(cn), (unsigned long)val if (0 != dk4str8_cpy_s(kb, sizeof(kb), printqd_kw[kwi], NULL)) { if (0 != dk4str8_cat_s(kb, sizeof(kb), cn, NULL)) { if (0 != dk4str8_cat_s(kb, sizeof(kb), printqd_kw[8], NULL)) { if (0 != dk4str8_cat_s(kb, sizeof(kb), un, NULL)) { if ((dk4_um_t)0UL != val) { if(0 != dk4ma_write_c8_decimal_unsigned(vb,sizeof(vb),val,0,NULL)) { strok = 1; $? ". db set \"%s\"=\"%s\"", kb, vb if (0 != dbi_set(db, kb, vb, NULL)) { $? ". saved" back = 1; } else { $? "! db set" /* ERROR: Failed to set DB entry */ log_1(NULL, 0UL, LOG_EMERG, 1, 117); } } #if TRACE_DEBUG else { $? "! to text" } #endif } else { strok = 1; $? ". db del \"%s\"", kb if (0 != dbi_del(db, kb, NULL)) { $? ". deleted" back = 1; } else { $? "! db del" /* ERROR: Failed to delete DB entry */ log_1(NULL, 0UL, LOG_EMERG, 1, 118); } } } } } } if (0 == strok) { /* ##### ERROR: Strings too long */ } $? "- save_one_number %d", back return back; } /** Save changes in account to database. @param oldacc Previous account information, may be NULL. @param newacc Updated account information, not NULL. @param un User name. @param cl Class structure. @return 1 on success, 0 on error. */ static int save_account_for_user_and_class( pqd_account_t *oldacc, pqd_account_t *newacc, const char *un, pqd_class_t *cl ) { int mustsave; /* Flag: Must save item */ int back = 1; $? "+ save_account_for_user_and_class" mustsave = 1; if (NULL != oldacc) { if (oldacc->used == newacc->used) { mustsave = 0; } } if (0 != mustsave) { $? ". must save pages" if (0 == save_one_number(88, un, cl->name, newacc->used)) { back = 0; } } mustsave = 1; if (NULL != oldacc) { if (oldacc->account == newacc->account) { mustsave = 0; } } if (0 != mustsave) { $? ". must save account" if (0 == save_one_number(89, un, cl->name, newacc->account)) { back = 0; } } $? "- save_account_for_user_and_class %d", back return back; } /** Retrieve account data for a user in a specified printer class. @param account Data structure to fill. @param un User name. @param cl Printer class. @param mi Flag: Must initialize account before filling it. @return 1 on success, 0 on error. */ static int retrieve_account_for_user_and_class( pqd_account_t *account, const char *un, pqd_class_t *cl, int mi ) { char **grmem = NULL; const char *ep = NULL; /* End of data */ pqd_limit_t *lim = NULL; /* Limit set */ struct passwd *pw = NULL; /* Password struct */ struct group *gr = NULL; /* Group struct */ dk4_um_t um = (dk4_um_t)0UL; /* Value found in text */ gid_t pg = 0; /* Primary group */ int ingr = 0; /* Flag: User in group */ int back = 0; $? "+ retrieve_account_for_user_and_class \"%s\" %s", TR_8STR(un), TR_PTR(cl) /* Check arguments */ if (NULL == account) { goto finished; } if (0 != mi) { account_init(account); } if ((NULL == un) || (NULL == cl)) { goto finished; } if (NULL == cl->name) { goto finished; } /* Use default action from class */ account->da = cl->da; /* Find number of pages already printed */ if (0 != dk4str8_cpy_s(kb, sizeof(kb), printqd_kw[88], NULL)) { if (0 != dk4str8_cat_s(kb, sizeof(kb), cl->name, NULL)) { if (0 != dk4str8_cat_s(kb, sizeof(kb), printqd_kw[8], NULL)) { if (0 != dk4str8_cat_s(kb, sizeof(kb), un, NULL)) { $? ". db get \"%s\"", kb if (0 != dbi_get(db, kb, vb, sizeof(vb), NULL)) { if (0 != dk4ma_input_c8_dec_dk4_um_t(&um, vb, &ep, 1, NULL)) { account->used = um; } #if TRACE_DEBUG else { $? "! not a number" } #endif } #if TRACE_DEBUG else { $? "! db get" } #endif } } } } /* Find number of pages in personal print account */ if (0 != dk4str8_cpy_s(kb, sizeof(kb), printqd_kw[89], NULL)) { if (0 != dk4str8_cat_s(kb, sizeof(kb), cl->name, NULL)) { if (0 != dk4str8_cat_s(kb, sizeof(kb), printqd_kw[8], NULL)) { if (0 != dk4str8_cat_s(kb, sizeof(kb), un, NULL)) { $? ". db get \"%s\"", kb if (0 != dbi_get(db, kb, vb, sizeof(vb), NULL)) { um = (dk4_um_t)0UL; ep = NULL; if (0 != dk4ma_input_c8_dec_dk4_um_t(&um, vb, &ep, 1, NULL)) { account->account = um; } #if TRACE_DEBUG else { $? "! not a number" } #endif } #if TRACE_DEBUG else { $? "! db get" } #endif } } } } /* Find user limit */ $? ". check user limit" lim = (pqd_limit_t *)dk4sto_it_find_like(cl->i_u, un, 1); if (NULL != lim) { $? ". user limit found" account->limit = lim->limit; back = 1; goto finished; } /* Find group limit */ $? ". check group limits" pw = getpwnam(un); if (NULL != pw) { $? ". pw found" pg = pw->pw_gid; dk4sto_it_reset(cl->i_g); do { $? ". loop start" lim = dk4sto_it_next(cl->i_g); if (NULL != lim) { $? ". yet another group limit" if (lim->limit > account->limit) { $? ". group limit is higher" $? ". group=\"%s\" limit=%lu", TR_8STR(lim->name), (unsigned long)(lim->limit) gr = getgrnam(lim->name); if (NULL != gr) { $? ". gr found" if (pg == gr->gr_gid) { $? ". is users primary group" account->limit = lim->limit; back = 1; } else { $? ". not primary group" if (NULL != gr->gr_mem) { grmem = gr->gr_mem; ingr = 0; while ((0 == ingr) && (NULL != *grmem)) { $? ". compare \"%s\" \"%s\"", TR_8STR(un), TR_8STR(*grmem) if (0 == strcmp(un, *grmem)) { ingr = 1; } grmem++; } if (0 != ingr) { $? ". user is in group" account->limit = lim->limit; back = 1; } #if TRACE_DEBUG else { $? ". user is not in group" } #endif } #if TRACE_DEBUG else { $? ". no members in group" } #endif } } #if TRACE_DEBUG else { $? "! group not found" } #endif } } #if TRACE_DEBUG else { $? "! no more group limits" } #endif $? ". loop end" } while(NULL != lim); if (0 != back) { goto finished; } } #if TRACE_DEBUG else { $? "! getpwnam" } #endif /* Apply default limit */ $? ". apply default limit" account->limit = cl->dl; back = 1; finished: $? "- retrieve_account_for_user_and_class %d", back return back; } /** Retrieve user account data for a given user name and a given printer name. @param account Account structure to fill. @param printer_name Print queue name or alias. @param user_name User name. @return 1 on success, 0 on error. */ static int retrieve_account_data( pqd_account_t *account, const char *printer_name, const char *user_name ) { pqd_printer_t *pr = NULL; /* Printer */ pqd_class_t *cl = NULL; /* Class */ int back = 0; $? "+ retrieve_account_data p=\"%s\" u=\"%s\"", TR_8STR(printer_name), TR_8STR(user_name) /* Initialize account data */ account_init(account); /* Find printer */ pr = get_printer_for_name(printer_name); if (NULL == pr) { $? "! no such printer" goto finished; } /* Find class for printer */ cl = pr->cl; if (NULL == cl) { $? "! no such class" goto finished; } if (NULL == cl->name) { $? "! no class name" goto finished; } /* Search for data */ back = retrieve_account_for_user_and_class(account, user_name, cl, 0); /* Return result */ finished: $? "- retrieve_account_data %d", back return back; } /** Add a number of pages for a print job to existing account data. @param account Account data for user. @param pages Number of pages used by print job. */ static void account_add_pages_from_job(pqd_account_t *account, dk4_um_t pages) { dk4_um_t available; /* The limit is used first. */ if (account->limit > account->used) { available = account->limit - account->used; if (available >= pages) { account->used += pages; pages = (dk4_um_t)0UL; } else { account->used = account->limit; pages -= available; } } /* The personal print account is used only if the limit is reached. */ if ((dk4_um_t)0UL < pages) { if (account->account >= pages) { account->account -= pages; } else { pages -= account->account; account->account = (dk4_um_t)0UL; } } /* Finally keep track of pages printed over limit. */ if ((dk4_um_t)0UL < pages) { if ((DK4_UM_MAX - pages) >= account->used) { account->used += pages; } else { account->used = DK4_UM_MAX; } } } /** Find summary (allowed to print) for account details. @param limit Limit (maximum number of pages) for user. @param used Number of used pages within the limit. @param account Number of pages in additional personal print account. @return 1 if printing is allowed, 0 otherwise. */ static int account_summary(dk4_um_t limit, dk4_um_t used, dk4_um_t account) { int back = 0; $? "+ account_summary l=%lu u=%lu a=%lu", (unsigned long)limit, (unsigned long)used, (unsigned long)account if (DK4_UM_MAX == limit) { $? ". unlimited" back = 1; } else { if (used < limit) { $? ". limit not yet reached" back = 1; } else { if ((dk4_um_t)0UL < account) { $? ". positive account value" back = 1; } } } $? "- account_summary %d", back return back; } /** Process info or acct-check request, only retrieve data without modifying anything. @param job Data for one request to process. @param how Response type: 0=info, 1=acct-check. */ static void rq_retrieve_data(pqd_rq_t *job, int how) { pqd_account_t account; char *printer_name = NULL; char *user_name = NULL; int summary = 0; $? "+ rq_retrieve_data %d", how printer_name = job->argptr; if (NULL != printer_name) { user_name = dk4str8_next(printer_name, NULL); if (NULL != user_name) { (void)dk4str8_next(user_name, NULL); } } if ((NULL != printer_name) && (NULL != user_name)) { if (0 != retrieve_account_data(&account, printer_name, user_name)) { #if VERSION_BEFORE_20160329 if ((account.used < account.limit) || ((dk4_um_t)0UL < account.account)) { summary = 1; } #else summary = account_summary(account.limit, account.used, account.account); #endif } send_response(job, &account, summary, how); } else { /* Close connection on malformed request. */ if ((NULL == job->soa) && (0 == job->szsoa)) { close_stream_conn(job->sock, job->sto, job->connptr, job->pnconn); } } $? "- rq_retrieve_data" } /** Process info request. @param job Data for one request to process. */ static void rq_info(pqd_rq_t *job) { $? "+ rq_info" rq_retrieve_data(job, 0); $? "- rq_info" } /** Find final quote for current word. @param str String to find quote in. @param quote Quote character. @return Pointer to found quote on success, NULL on error. */ static char * lprng_final_quote(char *str, char quote) { char *back = NULL; $? "+ lprng_final_quote %c %s", quote, TR_8STR(str) while (('\0' != *str) && (NULL == back)) { if (quote == *str) { *str = '\0'; back = str; back++; back = dk4str8_start(back, NULL); } else { if ('\\' == *str) { if ('\0' != str[1]) { dk4str8_cpy_to_left(str, &(str[1])); str++; } else { *str = '\0'; } } else { str++; } } } $? "- lprng_final_quote \"%s\"", TR_8STR(back) return back; } /** Find start of next text word in LPRng accounting line. @param str Current word (remaining text) in accounting line. @return Pointer to next word if found, NULL on error. */ static char * lprng_next_word(char *str) { char *back = NULL; $? "+ lprng_next_word \"%s\"", TR_8STR(str) switch (*str) { case '\'' : { back = lprng_final_quote(&(str[1]), '\''); } break; case '"' : { back = lprng_final_quote(&(str[1]), '"'); } break; default : { back = dk4str8_next(str, NULL); } break; } $? "- lprng_next_word \"%s\"", TR_8STR(back) return back; } /** Fill data structure from LPRng accounting line. @param liptr Structure to fill. @param textline The text line to use. @return 1 on success, 0 on error. */ static int lprng_info_from_line(lprng_info_t *liptr, char *textline) { char *next; int back = 1; $? "+ lprng_info_from_line \"%s\"", TR_8STR(textline) DK4_MEMRES(liptr, sizeof(lprng_info_t)); liptr->username = NULL; liptr->queuename = NULL; liptr->jobtitle = NULL; liptr->pagecount = NULL; while (NULL != textline) { next = lprng_next_word(textline); if (('\'' == *textline) || ('"' == *textline)) { textline++; } if ('-' == *textline) { textline++; switch (*textline) { case 'n': { liptr->username = ++textline; } break; case 'P': { liptr->queuename = ++textline; } break; case 'J': { liptr->jobtitle = ++textline; } break; case 'p': { liptr->pagecount = ++textline; } break; } } else { $? "! not a minus" #if 0 /* 2016-02-15 Simply ignore non-option text. */ back = 0; /* ERROR: No minus sign */ #endif } textline = next; } $? "- lprng_info_from_line %d", back return back; } /** Process jobstart request. @param job Data for one request to process. */ static void rq_jobstart(pqd_rq_t *job) { lprng_info_t lpi; pqd_account_t account; char *un = NULL; char *qn = NULL; int summ = 0; $? "+ rq_jobstart" if (0 != lprng_info_from_line(&lpi, job->argptr)) { un = lpi.username; qn = lpi.queuename; } if ((NULL != un) && (NULL != qn)) { if (0 != retrieve_account_data(&account, qn, un)) { summ = account_summary(account.limit, account.used, account.account); } send_response(job, &account, summ, 1); } else { /* Close connection on malformed request. */ if ((NULL == job->soa) && (0 == job->szsoa)) { close_stream_conn(job->sock, job->sto, job->connptr, job->pnconn); } } $? "- rq_jobstart" } /** Report change result to log file. @param un User name. @param cl Class. @param olda Old account values. @param newa New account values. @param pages Number of pages to add. @param doadd Flag: Show added pages instead of used pages. */ static void log_old_and_new_account( const char *un, const pqd_class_t *cl, const pqd_account_t *olda, const pqd_account_t *newa, dk4_um_t pages, int doadd ) { char nb[8*sizeof(dk4_um_t)]; const char *msgs[2]; int res = 1; if (0 == dk4str8_cpy_s(kb, sizeof(kb), un, NULL)) { res = 0; } if (0 == dk4str8_cat_s(kb, sizeof(kb), printqd_kw[85], NULL)) { res = 0; } if (0 == dk4str8_cat_s(kb, sizeof(kb), cl->name, NULL)) { res = 0; } if (0 == dk4str8_cat_s(kb, sizeof(kb), printqd_kw[7], NULL)) { res = 0; } if (0 == dk4str8_cat_s(kb, sizeof(kb), printqd_kw[145], NULL)) { res = 0; } if (DK4_UM_MAX == olda->limit) { if (0 == dk4str8_cat_s(kb, sizeof(kb), printqd_kw[151], NULL)) { res = 0; } } else { if(0 != dk4ma_write_c8_decimal_unsigned(nb,sizeof(nb),olda->limit,0,NULL)) { if (0 == dk4str8_cat_s(kb, sizeof(kb), nb, NULL)) { res = 0; } } else { res = 0; } } if (0 == dk4str8_cat_s(kb, sizeof(kb), printqd_kw[146], NULL)) { res = 0; } if(0 != dk4ma_write_c8_decimal_unsigned(nb,sizeof(nb),olda->used,0,NULL)) { if (0 == dk4str8_cat_s(kb, sizeof(kb), nb, NULL)) { res = 0; } } else { res = 0; } if (0 == dk4str8_cat_s(kb, sizeof(kb), printqd_kw[147], NULL)) { res = 0; } if(0 != dk4ma_write_c8_decimal_unsigned(nb,sizeof(nb),olda->account,0,NULL)) { if (0 == dk4str8_cat_s(kb, sizeof(kb), nb, NULL)) { res = 0; } } else { res = 0; } switch (doadd) { case 1: { if (0 == dk4str8_cat_s(kb,sizeof(kb),printqd_kw[153],NULL)) { res = 0; } } break; default: { if (0 == dk4str8_cat_s(kb,sizeof(kb),printqd_kw[148],NULL)) { res = 0; } } break; } if(0 != dk4ma_write_c8_decimal_unsigned(nb,sizeof(nb),pages,0,NULL)) { if (0 == dk4str8_cat_s(kb, sizeof(kb), nb, NULL)) { res = 0; } } else { res = 0; } if (0 == dk4str8_cat_s(kb, sizeof(kb), printqd_kw[149], NULL)) { res = 0; } if (DK4_UM_MAX == newa->limit) { if (0 == dk4str8_cat_s(kb, sizeof(kb), printqd_kw[151], NULL)) { res = 0; } } else { if(0 != dk4ma_write_c8_decimal_unsigned(nb,sizeof(nb),newa->limit,0,NULL)) { if (0 == dk4str8_cat_s(kb, sizeof(kb), nb, NULL)) { res = 0; } } else { res = 0; } } if (0 == dk4str8_cat_s(kb, sizeof(kb), printqd_kw[146], NULL)) { res = 0; } if(0 != dk4ma_write_c8_decimal_unsigned(nb,sizeof(nb),newa->used,0,NULL)) { if (0 == dk4str8_cat_s(kb, sizeof(kb), nb, NULL)) { res = 0; } } else { res = 0; } if (0 == dk4str8_cat_s(kb, sizeof(kb), printqd_kw[147], NULL)) { res = 0; } if(0 != dk4ma_write_c8_decimal_unsigned(nb,sizeof(nb),newa->account,0,NULL)) { if (0 == dk4str8_cat_s(kb, sizeof(kb), nb, NULL)) { res = 0; } } else { res = 0; } if (0 == dk4str8_cat_s(kb, sizeof(kb), printqd_kw[150], NULL)) { res = 0; } if (0 != res) { msgs[0] = kb; msgs[1] = NULL; log_multipart_message(NULL, 0UL, LOG_EMERG, 0, msgs, 1); } } /** Charge user for a print job. @param job Job structure. @param un User name. @param cl Printer class of printer the job was printed on. @param pcval Number of pages in print job. */ static void do_charge( pqd_rq_t *job, const char *un, pqd_class_t *cl, dk4_um_t pcval ) { pqd_account_t oldacc; pqd_account_t newacc; $? "+ do_charge %s \"%s\" \"%s\" %lu", TR_8PTR(job), un, TR_8STR(cl->name), (unsigned long)pcval if (0 != retrieve_account_for_user_and_class(&oldacc, un, cl, 1)) { DK4_MEMCPY(&newacc, &oldacc, sizeof(pqd_account_t)); account_add_pages_from_job(&newacc, pcval); log_old_and_new_account(un, cl, &oldacc, &newacc, pcval, 0); if (0 == save_account_for_user_and_class(&oldacc, &newacc, un, cl)) { $? "! failed to save updated account data" /* ERROR: Failed to update account data */ log_5(NULL, 0UL, LOG_EMERG, 1, 156, 157, 158, un, cl->name); } } #if TRACE_DEBUG else { $? "! failed to retrieve existing account data" } #endif $? "- do_charge" } /** On start of file printing save user name, job name and pagecount value to database for later use. @param job Job structure. @param un User name. @param pn Printer name. @param jn Job name. @param pc Printer page count at start of job. */ static void do_filestart( pqd_rq_t *job, const char *un, const char *pn, const char *jn, const char *pc ) { const char *ep = NULL; pqd_printer_t *pr = NULL; pqd_class_t *cl = NULL; dk4_um_t pcstt = (dk4_um_t)0UL; int res = 0; $? "+ do_filestart %s \"%s\" \"%s\" \"%s\" \"%s\"", TR_8PTR(job),un,pn,jn,pc pr = get_printer_for_name(pn); if (NULL != pr) { cl = pr->cl; } if (NULL != cl) { res = dk4ma_input_c8_dec_dk4_um_t(&pcstt, pc, &ep, 1, NULL); } if ((NULL != pr) && (NULL != cl) && (0 != res)) { $? ". pr=\"%s\" cl=\"%s\"", pr->name, TR_8STR(cl->name) if (0 == dk4str8_cpy_s(kb, sizeof(kb), printqd_kw[90], NULL)) { res = 0; } if (0 == dk4str8_cat_s(kb, sizeof(kb), pr->name, NULL)) { res = 0; } if (0 == dk4str8_cpy_s(vb, sizeof(vb), pc, NULL)) { res = 0; } if (0 == dk4str8_cat_s(vb, sizeof(vb), printqd_kw[8], NULL)) { res = 0; } if (0 == dk4str8_cat_s(vb, sizeof(vb), un, NULL)) { res = 0; } if (0 == dk4str8_cat_s(vb, sizeof(vb), printqd_kw[8], NULL)) { res = 0; } if (0 == dk4str8_cat_s(vb, sizeof(vb), jn, NULL)) { res = 0; } if (0 != res) { $? ". db set \"%s\"=\"%s\"", kb, vb if (0 == dbi_set(db, kb, vb, NULL)) { $? "! db set" /* ERROR: Failed to set database entry */ log_1(NULL, 0UL, LOG_EMERG, 1, 117); } } #if TRACE_DEBUG else { $? "! key or value name too long" } #endif } else { $? "! malformed request" /* Close connection on malformed request */ if ((NULL == job->soa) && (0 == job->szsoa)) { close_stream_conn(job->sock, job->sto, job->connptr, job->pnconn); } } $? "- do_filestart" } /** On end of file calculate page number used by the job and save modified account to database. @param job Job structure. @param un User name. @param pn Printer name. @param jn Job name. @param pc Printer page count at start of job. */ static void do_fileend( pqd_rq_t *job, const char *un, const char *pn, const char *jn, const char *pc ) { const char *ep = NULL; /* End of processed text */ pqd_printer_t *pr = NULL; /* Printer */ pqd_class_t *cl = NULL; /* Printer class */ char *oun = NULL; /* Old user name */ char *opc = NULL; /* Old page counter value */ char *ojn = NULL; /* Old job name */ dk4_um_t pcstt = (dk4_um_t)0UL; /* Page counter at job start */ dk4_um_t pcend = (dk4_um_t)0UL; /* Page counter at job end */ int res = 0; $? "+ do_fileend %s \"%s\" \"%s\" \"%s\" \"%s\"", TR_8PTR(job), un, pn, jn, pc pr = get_printer_for_name(pn); if (NULL != pr) { cl = pr->cl; } if (NULL != cl) { res = dk4ma_input_c8_dec_dk4_um_t(&pcend, pc, &ep, 1, NULL); } if ((NULL != pr) && (NULL != cl) && (0 != res)) { if (0 == dk4str8_cpy_s(kb, sizeof(kb), printqd_kw[90], NULL)) { res = 0; } if (0 == dk4str8_cat_s(kb, sizeof(kb), pr->name, NULL)) { res = 0; } if (0 != res) { $? ". db get \"%s\"", kb if (0 != dbi_get(db, kb, vb, sizeof(vb), NULL)) { $? ". db del \"%s\"", kb if (0 == dbi_del(db, kb, NULL)) { /* ERROR: Failed to delete db entry */ log_1(NULL, 0UL, LOG_EMERG, 1, 118); } $? ". db res \"%s\"", vb opc = dk4str8_start(vb, NULL); if (NULL != opc) { oun = dk4str8_chr(opc, ':'); if (NULL != oun) { *(oun++) = '\0'; oun = dk4str8_start(oun, NULL); } } if (NULL != oun) { ojn = dk4str8_chr(oun, ':'); if (NULL != ojn) { *(ojn++) = '\0'; ojn = dk4str8_start(ojn, NULL); } } if ((NULL != opc) && (NULL != oun) && (NULL != ojn)) { $? ". user \"%s\", old \"%s\"", un, oun $? ". job \"%s\", old \"%s\"", jn, ojn $? ". page count \"%s\", old \"%s\"", pc, opc if (0 == strcmp(ojn, jn)) { if (0 == strcmp(oun, un)) { ep = NULL; res = dk4ma_input_c8_dec_dk4_um_t(&pcstt, opc, &ep, 1, NULL); if (0 != res) { if (pcend > pcstt) { $? ". charge user" do_charge(job, un, cl, (pcend - pcstt)); } #if TRACE_DEBUG else { $? "! pagecount unchanged" } #endif } #if TRACE_DEBUG else { $? "! old pagecount not numeric" } #endif } #if TRACE_DEBUG else { $? "! user name mismatch" } #endif } #if TRACE_DEBUG else { $? "! job name mismatch" } #endif } #if TRACE_DEBUG else { $? "! incomplete entry" } #endif } else { $? "! not found" $? ". db del \"%s\"", kb if (0 == dbi_del(db, kb, NULL)) { /* ERROR: Failed to delete db entry */ log_1(NULL, 0UL, LOG_EMERG, 1, 118); } } } #if TRACE_DEBUG else { $? "! printer name too long" } #endif } else { $? "! malformed request" /* Close connection on malformed request */ if ((NULL == job->soa) && (0 == job->szsoa)) { close_stream_conn(job->sock, job->sto, job->connptr, job->pnconn); } } $? "- do_fileend" } /** Process filestart request. @param job Data for one request to process. */ static void rq_filestart(pqd_rq_t *job) { lprng_info_t lpi; char *un = NULL; char *pn = NULL; char *jn = NULL; char *pc = NULL; $? "+ rq_filestart" if (0 != lprng_info_from_line(&lpi, job->argptr)) { un = lpi.username; pn = lpi.queuename; jn = lpi.jobtitle; pc = lpi.pagecount; } if ((NULL != un) && (NULL != pn) && (NULL != jn) && (NULL != pc)) { do_filestart(job, un, pn, jn, pc); } else { /* Close connection on malformed request */ if ((NULL == job->soa) && (0 == job->szsoa)) { close_stream_conn(job->sock, job->sto, job->connptr, job->pnconn); } } $? "- rq_filestart" } /** Process fileend request. @param job Data for one request to process. */ static void rq_fileend(pqd_rq_t *job) { lprng_info_t lpi; char *un = NULL; char *pn = NULL; char *jn = NULL; char *pc = NULL; $? "+ rq_fileend" if (0 != lprng_info_from_line(&lpi, job->argptr)) { un = lpi.username; pn = lpi.queuename; jn = lpi.jobtitle; pc = lpi.pagecount; } if ((NULL != un) && (NULL != pn) && (NULL != jn) && (NULL != pc)) { do_fileend(job, un, pn, jn, pc); } else { /* Close connection on malformed request */ if ((NULL == job->soa) && (0 == job->szsoa)) { close_stream_conn(job->sock, job->sto, job->connptr, job->pnconn); } } $? "- rq_fileend" } /** Process acct-check request. @param job Data for one request to process. */ static void rq_acct_check(pqd_rq_t *job) { $? "+ rq_acct_check" rq_retrieve_data(job, 1); $? "- rq_acct_check" } /** Process acct-start request. @param job Data for one request to process. */ static void rq_acct_start(pqd_rq_t *job) { char *un = NULL; char *pn = NULL; char *jn = NULL; char *pc = NULL; $? "+ rq_acct_start" pn = job->argptr; if (NULL != pn) { un = dk4str8_next(pn, NULL); } if (NULL != un) { pc = dk4str8_next(un, NULL); } if (NULL != pc) { jn = dk4str8_next(pc, NULL); } if (NULL != jn) { (void)dk4str8_next(jn, NULL); } if ((NULL != un) && (NULL != pn) && (NULL != jn) && (NULL != pc)) { do_filestart(job, un, pn, jn, pc); } else { /* Close connection on malformed request */ if ((NULL == job->soa) && (0 == job->szsoa)) { close_stream_conn(job->sock, job->sto, job->connptr, job->pnconn); } } $? "- rq_acct_start" } /** Process acct-end request. @param job Data for one request to process. */ static void rq_acct_end(pqd_rq_t *job) { char *un = NULL; char *pn = NULL; char *jn = NULL; char *pc = NULL; $? "+ rq_acct_end" pn = job->argptr; if (NULL != pn) { un = dk4str8_next(pn, NULL); } if (NULL != un) { pc = dk4str8_next(un, NULL); } if (NULL != pc) { jn = dk4str8_next(pc, NULL); } if (NULL != jn) { (void)dk4str8_next(jn, NULL); } if ((NULL != un) && (NULL != pn) && (NULL != jn) && (NULL != pc)) { do_fileend(job, un, pn, jn, pc); } else { /* Close connection on malformed request */ if ((NULL == job->soa) && (0 == job->szsoa)) { close_stream_conn(job->sock, job->sto, job->connptr, job->pnconn); } } $? "- rq_acct_end" } /** Process acct-charge request. @param job Data for one request to process. */ static void rq_acct_charge(pqd_rq_t *job) { const char *ep = NULL; pqd_printer_t *pr = NULL; pqd_class_t *cl = NULL; char *un = NULL; char *pn = NULL; char *pc = NULL; dk4_um_t pcval = (dk4_um_t)0UL; $? "+ rq_acct_charge" pn = job->argptr; if (NULL != pn) { un = dk4str8_next(pn, NULL); } if (NULL != un) { pc = dk4str8_next(un, NULL); } if (NULL != pc) { (void)dk4str8_next(pc, NULL); } if (NULL != pn) { pr = get_printer_for_name(pn); } if (NULL != pr) { cl = pr->cl; } if ( (NULL != un) && (NULL != pn) && (NULL != pc) && (NULL != pr) && (NULL != cl) ) { if (0 != dk4ma_input_c8_dec_dk4_um_t(&pcval, pc, &ep, 1, NULL)) { do_charge(job, un, cl, pcval); } else { /* Close connection on malformed request */ if ((NULL == job->soa) && (0 == job->szsoa)) { close_stream_conn(job->sock, job->sto, job->connptr, job->pnconn); } } } else { /* Close connection on malformed request */ if ((NULL == job->soa) && (0 == job->szsoa)) { close_stream_conn(job->sock, job->sto, job->connptr, job->pnconn); } } $? "- rq_acct_charge" } /** Object for DB traversal in control requests. */ typedef struct { dk4_sto_t *s; /**< Key name storage. */ dk4_sto_it_t *i; /**< Key name storage iterator. */ char *cn; /**< Class name. */ char *un; /**< User name. */ } pqd_control_t; /** Comparison function for storage. @param l Left object. @param r Right object. @param cr Comparison criteria (ignored). @return Comparison result. */ static int db_entry_compare(const void *l, const void *r, int cr) { int back = 0; if (NULL != l) { if (NULL != r) { back = strcmp((const char *)l, (const char *)r); if (-1 > back) back = -1; if ( 1 < back) back = 1; } else { back = 1; } } else { if (NULL != r) { back = -1; } } return back; } /** Traverse simple database containing text. @param obj Object to modify during traversal. @param k Key text. @param v Value text. @return 1 on success, 0 on error (continue traversal), -1 on error (abort). */ static int rq_c_reset_traverse(void *obj, const char *k, const char *v) { pqd_control_t *ctoptr = NULL; /* Traversal object pointer */ char *p1 = NULL; /* DB entry type */ char *p2 = NULL; /* Class name */ char *p3 = NULL; /* User name */ int clm = 0; /* Flag: Class name matches */ int unm = 0; /* Flag: User name matches */ int back = 1; $? "+ rq_c_reset_traverse \"%s\"=\"%s\"", TR_8STR(k), TR_8STR(v) ctoptr = (pqd_control_t *)obj; if (0 != dk4str8_cpy_s(kb, sizeof(kb), k, NULL)) { p1 = dk4str8_start(kb, NULL); if (NULL != p1) { p2 = strchr(p1, ':'); if (NULL != p2) { *(p2++) = '\0'; p2 = dk4str8_start(p2, NULL); } if (NULL != p2) { if (0 == strcmp(p1, printqd_kw[152])) { p3 = strchr(p2, ':'); if (NULL != p3) { *(p3++) = '\0'; p3 = dk4str8_start(p3, NULL); } if (NULL != p3) { if (NULL != ctoptr->cn) { if (0 == strcmp(ctoptr->cn, p2)) { clm = 1; } } else { clm = 1; } if (0 != clm) { if (NULL != ctoptr->un) { if (0 == strcmp(ctoptr->un, p3)) { unm = 1; } } else { unm = 1; } if (0 != unm) { p1 = dk4str8_dup(k, NULL); if (NULL != p1) { if (0 == dk4sto_add(ctoptr->s, p1, NULL)) { dk4mem_free(p1); $? "! sto add" } } #if TRACE_DEBUG else { $? "! insufficient memory" } #endif } #if TRACE_DEBUG else { $? "! user name mismatch" } #endif } #if TRACE_DEBUG else { $? "! class name mismatch" } #endif } #if TRACE_DEBUG else { $? "! no third component" } #endif } #if TRACE_DEBUG else { $? "! not a p entry" } #endif } #if TRACE_DEBUG else { $? "! no second component" } #endif } #if TRACE_DEBUG else { $? "! empty" } #endif } if (0 == can_continue(0, 0)) { back = -1; } $? "- rq_c_reset_traverse %d", back return back; } /** Traverse simple database containing text. @param obj Object to modify during traversal. @param k Key text. @param v Value text. @return 1 on success, 0 on error (continue traversal), -1 on error (abort). */ static int rc_c_dbcl_traverse(void *obj, const char *k, const char *v) { pqd_control_t *pctobj; pqd_class_t *cl; char *p1; /* First argument */ char *p2; /* Second argument */ int cdel = 0; /* Flag: Can delete item */ int back = 1; if (0 != dk4str8_cpy_s(kb, sizeof(kb), k, NULL)) { if (0 != dk4str8_cpy_s(vb, sizeof(vb), k, NULL)) { p1 = strchr(kb, ':'); if (NULL != p1) { *(p1++) = '\0'; p1 = dk4str8_start(p1, NULL); dk4str8_normalize(kb, NULL); if (NULL != p1) { pctobj = (pqd_control_t *)obj; switch ( dk4str8_array_index(db_type_names, kb, 0) ) { case 0 : /* p:class:user */ case 1 : /* a:class:user */ { p2 = strchr(p1, ':'); if (NULL != p2) { *(p2++) = '\0'; p2 = dk4str8_start(p2, NULL); if (NULL != p2) { dk4str8_normalize(p1, NULL); dk4str8_normalize(p2, NULL); cl = dk4sto_it_find_like(conf.i_c, p1, 1); if (NULL != cl) { if (NULL == dk4sto_it_find_like(cl->i_u, p2, 1)) { if (NULL == getpwnam(p2)) { cdel = 1; } } } else { cdel = 1; } if (0 != cdel) { p1 = dk4str8_dup(vb, NULL); if (NULL != p1) { if (0 == dk4sto_add(pctobj->s, p1, NULL)) { dk4mem_free(p1); back = 0; } } else { back = 0; } } } } } break; case 2 : { /* j:printer */ dk4str8_normalize(p1, NULL); if (NULL == get_printer_for_name(p1)) { p1 = dk4str8_dup(vb, NULL); if (NULL != p1) { if (0 == dk4sto_add(pctobj->s, p1, NULL)) { dk4mem_free(p1); back = 0; } } else { back = 0; } } } break; } } } } } if (0 == can_continue(0, 0)) { back = -1; } return back; } /** Process control reset request. @param job Arguments for request. */ static void rq_c_reset(pqd_rq_t *job) { pqd_control_t ctobj; char *pc; char *pn; char *pv; $? "+ rq_c_reset" /* Initialize variable */ ctobj.s = NULL; ctobj.i = NULL; ctobj.cn = NULL; ctobj.un = NULL; /* Process command arguments */ pc = job->argptr; while (NULL != pc) { pn = dk4str8_next(pc, NULL); $? ". pc=\"%s\" pn=\"%s\"", pc, TR_8STR(pn) pv = dk4str8_chr(pc, '='); if (NULL != pv) { *(pv++) = '\0'; pv = dk4str8_start(pv, NULL); } $? ". pc=\"%s\" pv=\"%s\"", pc, TR_8STR(pv) if (NULL != pv) { switch ( dk4str8_abbr_index(control_arg_names, '$', pc, 0) ) { case 0 : { ctobj.cn = pv; } break; case 1 : { ctobj.un = pv; } break; default : { $? ". unknown \"%s\"=\"%s\"", pc, pv } break; } } pc = pn; } $? ". class=%s user=%s", TR_8STR(ctobj.cn), TR_8STR(ctobj.un) if (NULL != ctobj.cn) { if (0 == dk4str8_cmp(printqd_kw[2], ctobj.cn)) { ctobj.cn = NULL; } } if (NULL != ctobj.un) { if (0 == dk4str8_cmp(printqd_kw[2], ctobj.un)) { ctobj.un = NULL; } } ctobj.s = dk4sto_open(NULL); if (NULL != ctobj.s) { $? ". storage" dk4sto_set_comp(ctobj.s, db_entry_compare, 0); ctobj.i = dk4sto_it_open(ctobj.s, NULL); if (NULL != ctobj.i) { $? ". iterator" (void)dk4dbi_c8_traverse(db, (void *)(&ctobj), rq_c_reset_traverse); $? ". traversal finished" dk4sto_it_reset(ctobj.i); do { pc = (char *)dk4sto_it_next(ctobj.i); if (NULL != pc) { $? ". delete \"%s\"", pc if (0 != can_continue(0, 0)) { (void)dbi_del(db, pc, NULL); } dk4mem_free(pc); } } while (NULL != pc); dk4sto_it_close(ctobj.i); } #if TRACE_DEBUG else { $? "! iterator" } #endif dk4sto_close(ctobj.s); } #if TRACE_DEBUG else { $? "! storage" } #endif $? "- rq_c_reset" } /** Process control add request. @param job Arguments for request. */ static void rq_c_add(pqd_rq_t *job) { pqd_account_t oldacc; pqd_account_t newacc; pqd_class_t *cl = NULL; const char *ep = NULL; char *clname = NULL; char *uname = NULL; char *ptext = NULL; char *pc; char *pn; char *pv; dk4_um_t pages = (dk4_um_t)0UL; /* Process command arguments */ pc = job->argptr; while (NULL != pc) { pn = dk4str8_next(pc, NULL); pv = dk4str8_chr(pc, '='); if (NULL != pv) { *(pv++) = '\0'; pv = dk4str8_start(pv, NULL); } if (NULL != pv) { switch ( dk4str8_abbr_index(control_arg_names, '$', pc, 0) ) { case 0 : { clname = pv; } break; case 1 : { uname = pv; } break; case 2 : { ptext = pv; } break; } } pc = pn; } if ((NULL != clname) && (NULL != uname) && (NULL != ptext)) { if (0 != dk4ma_input_c8_dec_dk4_um_t(&pages, ptext, &ep, 1, NULL)) { cl = dk4sto_it_find_like(conf.i_c, clname, 1); if (NULL != cl) { if (0 != retrieve_account_for_user_and_class(&oldacc, uname, cl, 1)) { DK4_MEMCPY(&newacc, &oldacc, sizeof(pqd_account_t)); if ((DK4_UM_MAX - pages) >= newacc.account) { newacc.account += pages; } else { newacc.account = DK4_UM_MAX; } log_old_and_new_account(uname, cl, &oldacc, &newacc, pages, 1); if (0 == save_account_for_user_and_class(&oldacc,&newacc,uname,cl)) { /* ERROR: Failed to save new account */ log_5(NULL, 0UL, LOG_EMERG, 1, 156, 157, 158, uname, cl->name); } } else { /* ERROR: Failed to retrieve account data */ log_5(NULL, 0UL, LOG_EMERG, 1, 161, 162, 163, uname, cl->name); } } else { /* ERROR: No such class */ log_3(NULL, 0UL, LOG_EMERG, 1, 164, 165, clname); } } else { /* ERROR: ptext not numeric */ log_3(NULL, 0UL, LOG_EMERG, 1, 166, 167, ptext); } } } /** Process control database-cleanup request. @param job Arguments for request. */ static void rq_c_dbcl(pqd_rq_t *job) { pqd_control_t ctobj; char *ptr; DK4_MEMRES(&ctobj, sizeof(pqd_control_t)); ctobj.s = NULL; ctobj.i = NULL; ctobj.cn = NULL; ctobj.un = NULL; ctobj.s = dk4sto_open(NULL); if (NULL != ctobj.s) { dk4sto_set_comp(ctobj.s, db_entry_compare, 0); ctobj.i = dk4sto_it_open(ctobj.s, NULL); if (NULL != ctobj.i) { (void)dk4dbi_c8_traverse(db, (void *)(&ctobj), rc_c_dbcl_traverse); dk4sto_it_reset(ctobj.i); do { ptr = (char *)dk4sto_it_next(ctobj.i); if (NULL != ptr) { if (0 != can_continue(0, 0)) { dbi_del(db, ptr, NULL); } dk4mem_free(ptr); } } while(NULL != ptr); dk4sto_it_close(ctobj.i); } dk4sto_close(ctobj.s); } } /** Process control request. @param job Data for one request to process. */ static void rq_control(pqd_rq_t *job) { char *subcmd; int sc; $? "+ rq_control" subcmd = job->argptr; job->argptr = dk4str8_next(subcmd, NULL); $? ". argptr = \"%s\"", TR_8STR(job->argptr) switch ( (sc = dk4str8_abbr_index(control_sub_cmds, '$', subcmd, 0)) ) { case 0 : { $? ". reset" rq_c_reset(job); } break; case 1 : { $? ". add" rq_c_add(job); } break; case 2 : { $? ". database-cleanup" rq_c_dbcl(job); } break; default : { /* ERROR: Illegal sub-command */ log_3(NULL, 0UL, LOG_EMERG, 1, 168, 169, subcmd); } break; } $? "- rq_control" } /** Process contents of the inbuf buffer, send response if necessary. @param job Data for one request to process. */ static void process_data(pqd_rq_t *job) { const char * msgs[3]; $? "+ process_data sock=%d level=%d addr=%s", job->sock, job->protlev, TR_8PTR(job->soa) job->cmdptr = dk4str8_start(inbuf, NULL); if (NULL != job->cmdptr) { dk4str8_delnl(job->cmdptr); /* LOG request */ if (0 == conf.linf) { if ('i' == (job->cmdptr)[0]) { if ('n' == (job->cmdptr)[1]) { if ('f' == (job->cmdptr)[2]) { if ('o' == (job->cmdptr)[3]) { job->logthis = 0; } } } } } if (0 != job->logthis) { msgs[0] = printqd_kw[87]; msgs[1] = job->cmdptr; msgs[2] = NULL; log_multipart_message(NULL, 0UL, LOG_EMERG, 0, msgs, 2); } /* Process request */ job->argptr = dk4str8_next(job->cmdptr, NULL); job->action = dk4str8_array_index(pqd_command_names, job->cmdptr, 0); if (0 != protocol_level_check(job->action, job->protlev)) { switch (job->action) { case PQD_CMD_INFO : { rq_info(job); } break; case PQD_CMD_JOBSTART : { rq_jobstart(job); } break; case PQD_CMD_FILESTART : { rq_filestart(job); } break; case PQD_CMD_FILEEND : { rq_fileend(job); } break; case PQD_CMD_ACCT_CHECK : { rq_acct_check(job); } break; case PQD_CMD_ACCT_START : { rq_acct_start(job); } break; case PQD_CMD_ACCT_END : { rq_acct_end(job); } break; case PQD_CMD_ACCT_CHARGE : { rq_acct_charge(job); } break; case PQD_CMD_CONTROL : { rq_control(job); } break; } } else { /* Close connection if request is denied. */ if ((NULL == job->soa) && (0 == job->szsoa)) { close_stream_conn(job->sock, job->sto, job->connptr, job->pnconn); } } } $? "- process_data" } /** Run one pass within inner loop. */ static void one_pass(void) { fd_set rfds; /* Read file desc set */ dk4_sockaddr_storage_t soa_sto; /* Remote address */ pqd_rq_t job; /* Job structure */ struct sockaddr *soa; /* Remote address */ #if VERSION_BEFORE_20160329 struct sockaddr_in *soa4; /* Remote IPv4 address */ #if DK4_HAVE_STRUCT_SOCKADDR_IN6 struct sockaddr_in6 *soa6; /* Remote IPv6 address */ #endif #endif pqd_l_conn_t *pconnloc; /* UNIX connection record */ pqd_n_conn_t *pconntcp; /* TCP connection record */ size_t sz_soa_sto; /* Address size */ size_t rdbytes; /* Number of bytes read */ size_t i; /* Traverse socket set */ dk4_socket_t sock; /* Current new socket */ int maxfd = 0; /* Maximum fd number */ int l_conn = 0; /* Flag: Local connection */ int l_proc = 0; /* Flag: Local processing */ int res = 0; /* Operation result */ int addrok; /* Flag: Address size ok */ int protlev; /* Procotol level for conn */ $? "+ one_pass" /* Create file descriptor set for select. */ FD_ZERO(&rfds); /* Check for incoming UNIX conn rq only if maximum not yet reached. */ if ((0 == conf.m_loc) || (con_loc < conf.m_loc)) { FD_SET(so_unix,&rfds); if (maxfd < so_unix) { maxfd = so_unix; } } /* Check for incoming TCP conn rq only if maximum not yet reached. */ if ((NULL != ss_tcp) && ((0 == conf.m_tcp) || (con_tcp < conf.m_tcp))) { for (i = 0; i < ss_tcp->szUsed; i++) { FD_SET((ss_tcp->pSockets)[i],&rfds); if (maxfd < (ss_tcp->pSockets)[i]) { maxfd = (ss_tcp->pSockets)[i]; } } } /* Check for data on existing UNIX connections. */ dk4sto_it_reset(i_c_unix); do { pconnloc = (pqd_l_conn_t *)dk4sto_it_next(i_c_unix); if (NULL != pconnloc) { FD_SET(pconnloc->sock,&rfds); if (maxfd < pconnloc->sock) { maxfd = pconnloc->sock; } } } while (NULL != pconnloc); /* Check for data on existing TCP connections. */ dk4sto_it_reset(i_c_tcp); do { pconntcp = (pqd_n_conn_t *)dk4sto_it_next(i_c_tcp); if (NULL != pconntcp) { FD_SET(pconntcp->sock,&rfds); if (maxfd < pconntcp->sock) { maxfd = pconntcp->sock; } } } while (NULL != pconntcp); /* Check for data on UDP sockets. */ if (NULL != ss_udp) { for (i = 0; i < ss_udp->szUsed; i++) { FD_SET((ss_udp->pSockets)[i],&rfds); if (maxfd < (ss_udp->pSockets)[i]) { maxfd = (ss_udp->pSockets)[i]; } } } /* Check for signal meanwhile. */ if (0 == can_continue(1, 0)) { $? ". one_pass: interrupted" goto finished; } /* Run select(). */ errno = 0; res = select((1+maxfd), &rfds, NULL, NULL, NULL); if (-1 == res) { switch (errno) { case EINTR: { $? ". one_pass: select interrupted" goto finished; } break; default: { $? ". one_pass: error from select" /* ERROR: Serious error from select() function */ log_1(NULL, 0UL, LOG_EMERG, 1, 119); ccouter = -1; ccinner = -1; goto finished; } break; } } if (0 == res) { $? ". one_pass: no descriptors ready" goto finished; } /* Check for new connection attempts on local socket. */ if ((0 == conf.m_loc) || (con_loc < conf.m_loc)) { if (0 != (FD_ISSET(so_unix,&rfds))) { $? ". one_pass: local conn" sock = dk4socket_accept(so_unix, NULL, NULL, NULL); if (INVALID_SOCKET != sock) { pconnloc = dk4mem_new(pqd_l_conn_t,1,NULL); if (NULL != pconnloc) { pconnloc->sock = sock; if (dk4sto_add(s_c_unix, pconnloc, NULL)) { con_loc++; l_conn = 1; } else { dk4mem_free(pconnloc); (void)dk4socket_close(sock, NULL); } } else { (void)dk4socket_close(sock, NULL); /* ERROR: Memory */ log_1(NULL, 0UL, LOG_EMERG, 1, 120); ccouter = -1; ccinner = -1; } } } } /* Process data from local socket connections. */ dk4sto_it_reset(i_c_unix); do { if (0 != can_continue(1, 0)) { pconnloc = (pqd_l_conn_t *)dk4sto_it_next(i_c_unix); if (NULL != pconnloc) { if (0 != (FD_ISSET(pconnloc->sock,&rfds))) { l_proc = 1; $? ". one_pass: local data" rdbytes = sizeof(inbuf); res = dk4socket_recv( pconnloc->sock, inbuf, &rdbytes, 0, 0L, 0L, NULL ); if (0 != can_continue(0, 0)) { if (DK4_SOCKET_RESULT_SUCCESS == res) { if (0 < rdbytes) { if (sizeof(inbuf) > rdbytes) { inbuf[rdbytes] = '\0'; job_init(&job); job.sock = pconnloc->sock; job.protlev = PQD_PROTO_ADMIN; job.connptr = pconnloc; job.sto = s_c_unix; job.pnconn = &con_loc; process_data(&job); } } else { close_stream_conn(pconnloc->sock, s_c_unix, pconnloc, &con_loc); } } else { close_stream_conn(pconnloc->sock, s_c_unix, pconnloc, &con_loc); } } else { pconnloc = NULL; } } } } else { pconnloc = NULL; } } while (NULL != pconnloc); /* Skip TCP and UDP data if activity on higher priorized local sockets. */ if ((0 != l_conn) || (0 != l_proc) || (0 == can_continue(1, 0))) { $? ". one_pass: skip non-local processing" goto finished; } /* Check for new connection attempts on TCP. */ if ((NULL != ss_tcp) && ((0 == conf.m_tcp) || (con_tcp < conf.m_tcp))) { for (i = 0; ((i < ss_tcp->szUsed) && (0 != can_continue(1, 0))); i++) { if (0 != (FD_ISSET((ss_tcp->pSockets)[i],&rfds))) { $? ". one_pass: tcp conn" DK4_MEMRES(&soa_sto, sizeof(soa_sto)); sz_soa_sto = sizeof(soa_sto); sock = dk4socket_accept( (ss_tcp->pSockets)[i], (struct sockaddr *)(&soa_sto), &sz_soa_sto, NULL ); if (INVALID_SOCKET != sock) { #if VERSION_BEFORE_20160329 #if DK4_HAVE_STRUCT_SOCKADDR_IN6 soa6 = (struct sockaddr_in6 *)(&soa_sto); #endif soa4 = (struct sockaddr_in *)(&soa_sto); #endif soa = (struct sockaddr *)(&soa_sto); addrok = 0; if (AF_INET == soa->sa_family) { if (sizeof(struct sockaddr_in) == sz_soa_sto) { addrok = 1; } } #if DK4_HAVE_STRUCT_SOCKADDR_IN6 if (AF_INET6 == soa->sa_family) { if (sizeof(struct sockaddr_in6) == sz_soa_sto) { addrok = 1; } } #endif if (0 != addrok) { protlev = protocol_level_for_address(soa); if (PQD_PROTO_NONE < protlev) { $? ". allowed %d", protlev pconntcp = dk4mem_new(pqd_n_conn_t,1,NULL); if (NULL != pconntcp) { pconntcp->sock = sock; if (0 != dk4sto_add(s_c_tcp, pconntcp, NULL)) { #if VERSION_BEFORE_20160811 DK4_MEMCPY(&(pconntcp->raddr),&soa_sto,sizeof(&soa_sto)); #else DK4_MEMCPY(&(pconntcp->raddr),&soa_sto,sizeof(soa_sto)); #endif pconntcp->pqdpl = protlev; } else { dk4mem_free(pconntcp); (void)dk4socket_close(sock, NULL); /* ERROR: Memory */ log_1(NULL, 0UL, LOG_EMERG, 1, 120); ccouter = -1; ccinner = -1; } } else { (void)dk4socket_close(sock, NULL); /* ERROR: Memory */ log_1(NULL, 0UL, LOG_EMERG, 1, 120); ccouter = -1; ccinner = -1; } } else { $? "! denied client" (void)dk4socket_close(sock, NULL); } } else { $? "! size/family mismatch" (void)dk4socket_close(sock, NULL); } } } } } if (0 == can_continue(1, 0)) { $? ". one_pass: interrupted" goto finished; } /* Process data from TCP connections. */ dk4sto_it_reset(i_c_tcp); do { if (0 != can_continue(1, 0)) { pconntcp = (pqd_n_conn_t *)dk4sto_it_next(i_c_tcp); if (NULL != pconntcp) { if (0 != (FD_ISSET(pconntcp->sock,&rfds))) { $? ". one_pass: tcp data" rdbytes = sizeof(inbuf); res = dk4socket_recv( pconntcp->sock, inbuf, &rdbytes, 0, 0L, 0L, NULL ); if (0 != can_continue(0, 0)) { if (DK4_SOCKET_RESULT_SUCCESS == res) { if (0 < rdbytes) { if (sizeof(inbuf) > rdbytes) { inbuf[rdbytes] = '\0'; job_init(&job); job.sock = pconntcp->sock; job.protlev = pconntcp->pqdpl; job.connptr = pconntcp; job.sto = s_c_tcp; job.pnconn = &con_tcp; process_data(&job); } } else { close_stream_conn(pconntcp->sock, s_c_tcp, pconntcp, &con_tcp); } } else { close_stream_conn(pconntcp->sock, s_c_tcp, pconntcp, &con_tcp); } } else { } } } } else { pconntcp = NULL; } } while (NULL != pconntcp); if (0 == can_continue(1, 0)) { $? ". one_pass: interrupted" goto finished; } /* Process UDP data. */ if (NULL != ss_udp) { for (i = 0; ((i < ss_udp->szUsed) && (0 != can_continue(1, 0))); i++) { if (0 != (FD_ISSET((ss_udp->pSockets)[i],&rfds))) { $? ". one_pass: udp data" rdbytes = sizeof(inbuf); sz_soa_sto = sizeof(soa_sto); res = dk4socket_recvfrom( (ss_udp->pSockets)[i], inbuf, &rdbytes, 0, (struct sockaddr *)(&soa_sto), &sz_soa_sto, 0L, 0L, NULL ); if ((DK4_SOCKET_RESULT_SUCCESS == res) && (0 < rdbytes)) { if ((0 != can_continue(1, 0)) && (sizeof(inbuf) > rdbytes)) { inbuf[rdbytes] = '\0'; #if VERSION_BEFORE_20160329 #if DK4_HAVE_STRUCT_SOCKADDR_IN6 soa6 = (struct sockaddr_in6 *)(&soa_sto); #endif soa4 = (struct sockaddr_in *)(&soa_sto); #endif soa = (struct sockaddr *)(&soa_sto); addrok = 0; if (AF_INET == soa->sa_family) { if (sizeof(struct sockaddr_in) == sz_soa_sto) { addrok = 1; } } #if DK4_HAVE_STRUCT_SOCKADDR_IN6 if (AF_INET6 == soa->sa_family) { if (sizeof(struct sockaddr_in6) == sz_soa_sto) { addrok = 1; } } #endif if (0 != addrok) { protlev = protocol_level_for_address(soa); if (PQD_PROTO_NONE < protlev) { job_init(&job); job.sock = (ss_udp->pSockets)[i]; job.protlev = PQD_PROTO_INFO; job.soa = (struct sockaddr *)(&soa_sto); job.szsoa = sz_soa_sto; process_data(&job); } } } } } } } finished: $? ". end of pass, synchronize db" if (0 == dbi_sync(db, NULL)) { /* ERROR: Failed to synchronize database */ log_1(NULL, 0UL, LOG_EMERG, 1, 121); } if (0 == can_continue(1, 0)) { ccinner = 0; if (0 == can_continue(0, 0)) { ccouter = 0; } } $? "- one_pass" } /** Run two nested loops: The outer loop is only finished by SIGTERM or SIGINT to exit the program, the inner loop is also finished by SIGHUP to re-read the configuration. */ static void service_loops(void) { $!trace-init /tmp/printqd.deb $? "+ service_loops" /* LOG: Daemon running */ log_1(NULL, 0UL, LOG_INFO, 0, 73); if (outer_allocate_resources()) { /* Outer loop is left on SIGTERM or SIGINT. */ while(1 == ccouter) { if (1 == can_continue(0, 0)) { config_init(); if (0 != config_read()) { if (inner_allocate_resources()) { if (0 != change_group_and_user()) { /* Inner loop is left on SIGHUP, SIGTERM or SIGINT. */ ccinner = 1; *sig_pass_pointer(&sig_had_hup) = 0; prev_filelog_failed = 0; /* LOG: Entering service mode */ log_1(NULL, 0UL, LOG_INFO, 0, 75); while(1 == ccinner) { if (1 == can_continue(1, 0)) { $? ". yet another pass" one_pass(); } else { $? "! signal" ccinner = 0; if (0 == can_continue(0, 0)) { ccouter = 0; /* LOG: Exiting service mode */ log_1(NULL, 0UL, LOG_INFO, 0, 76); } else { /* LOG: Exiting service mode for reconfiguration */ log_1(NULL, 0UL, LOG_INFO, 0, 77); } } } /* LOG: Exited service mode */ log_1(NULL, 0UL, LOG_INFO, 0, 78); } else { $? "! failed to change user" ccouter = -1; } } else { $? "! inner resource allocation" ccouter = -1; } if (0 == inner_release_resources()) { $? "! inner resource release" ccouter = -1; } } else { $? "! configuration read" ccouter = -1; } config_release(); } else { $? "! signal" ccouter = 0; } } } #if TRACE_DEBUG else { $? "! outer resource allocation" } #endif outer_release_resources(); /* LOG: Daemon exiting */ log_1( NULL, 0UL, ((0 == ccouter) ? (LOG_INFO) : (LOG_ERR)), ((0 == ccouter) ? (0) : (1)), ((0 == ccouter) ? (74) : (187)) ); $? "- service_loops" $!trace-end } /** Run service in debug mode or normal mode. */ static void set_signal_handlers_and_run_service(void) { int loop_was_running = 0; #ifdef DK4_HAVE_SIGACTION #ifdef SIGHUP struct sigaction ohup; struct sigaction nhup; #endif #ifdef SIGPIPE struct sigaction opipe; struct sigaction npipe; #endif struct sigaction oint; struct sigaction nint; struct sigaction oterm; struct sigaction nterm; #else #ifdef SIGPIPE dk4_sig_handler_t *opipe = NULL; #endif #ifdef SIGHUP dk4_sig_handler_t *ohup = NULL; #endif dk4_sig_handler_t *oterm = NULL; dk4_sig_handler_t *oint = NULL; #endif #ifdef DK4_HAVE_SIGACTION #ifdef SIGHUP int shup = 0; #endif #ifdef SIGPIPE int spipe = 0; #endif int sint = 0; int sterm = 0; int s_f_i = 0; int s_f_r = 0; #endif /* Set signal handlers */ #if DK4_HAVE_SIGACTION #ifdef SIGPIPE DK4_MEMRES(&npipe, sizeof(npipe)); npipe.sa_handler = sig_handler_pipe; npipe.sa_flags = 0; if (0 != sigemptyset(&npipe.sa_mask)) { s_f_i = 1; goto finished; } if (0 != sigaddset(&npipe.sa_mask, SIGPIPE)) { s_f_i = 1; goto finished; } if (0 != sigaction(SIGPIPE, &npipe, &opipe)) { s_f_i = 1; goto finished; } spipe = 1; #endif #ifdef SIGHUP DK4_MEMRES(&nhup, sizeof(nhup)); nhup.sa_handler = sig_handler_hup; nhup.sa_flags = 0; if (0 != sigemptyset(&nhup.sa_mask)) { s_f_i = 1; goto finished; } if (0 != sigaddset(&nhup.sa_mask, SIGHUP)) { s_f_i = 1; goto finished; } if (0 != sigaction(SIGHUP, &nhup, &ohup)) { s_f_i = 1; goto finished; } shup = 1; #endif DK4_MEMRES(&nterm, sizeof(nterm)); nterm.sa_handler = sig_handler_term; nterm.sa_flags = 0; if (0 != sigemptyset(&nterm.sa_mask)) { s_f_i = 1; goto finished; } if (0 != sigaddset(&nterm.sa_mask, SIGTERM)) { s_f_i = 1; goto finished; } if (0 != sigaction(SIGTERM, &nterm, &oterm)) { s_f_i = 1; goto finished; } sterm = 1; DK4_MEMRES(&nint, sizeof(nint)); nint.sa_handler = sig_handler_int; nint.sa_flags = 0; if (0 != sigemptyset(&nint.sa_mask)) { s_f_i = 1; goto finished; } if (0 != sigaddset(&nint.sa_mask, SIGINT)) { s_f_i = 1; goto finished; } if (0 != sigaction(SIGINT, &nint, &oint)) { s_f_i = 1; goto finished; } sint = 1; #else #ifdef SIGPIPE opipe = sigset(SIGPIPE, sig_handler_pipe); #endif #ifdef SIGHUP ohup = sigset(SIGHUP, sig_handler_hup); #endif oint = sigset(SIGINT, sig_handler_int); oterm = sigset(SIGTERM, sig_handler_term); #endif service_loops(); loop_was_running = 1; finished: /* */ if (0 == loop_was_running) { log_1(NULL, 0UL, LOG_ERR, 1, 72); } /* Restore signal handlers */ #if DK4_HAVE_SIGACTION if (0 != s_f_i) { /* ERROR: Failed to install signal handlers */ log_1(NULL, 0UL, LOG_EMERG, 1, 68); exval = EXIT_FAILURE; } if (0 != sint) { if (0 != sigaction(SIGINT, &oint, NULL)) { s_f_r = 1; } } if (0 != sterm) { if (0 != sigaction(SIGTERM, &oterm, NULL)) { s_f_r = 1; } } #ifdef SIGHUP if (0 != shup) { if (0 != sigaction(SIGHUP, &ohup, NULL)) { s_f_r = 1; } } #endif #ifdef SIGPIPE if (0 != spipe) { if (0 != sigaction(SIGPIPE, &opipe, NULL)) { s_f_r = 1; } } #endif if (0 != s_f_r) { /* ERROR: Failed to restore signal handlers */ log_1(NULL, 0UL, LOG_EMERG, 1, 69); exval = EXIT_FAILURE; } #else if (NULL != oterm) { sigset(SIGTERM, oterm); } if (NULL != oint ) { sigset(SIGINT, oint ); } #ifdef SIGHUP if (NULL != ohup) { sigset(SIGHUP, ohup); } #endif #ifdef SIGPIPE if (NULL != opipe) { sigset(SIGPIPE, opipe); } #endif #endif } /** Program entry point. @param argc @param argv @return 0 on success, any other value indicates an error. */ int main(int argc, char *argv[]) { pid_t cpid; /* Child process PID */ int res; /* Operation result */ /* Process command line arguments. */ res = dk4opt_process_argv( options, szoptions, argc, argv, NULL, NULL, 1, 1, NULL ); if (0 == res) { exval = 2; /* Invalid or excess arguments */ goto finished; } /* Must be started as root. */ if (0 != geteuid()) { fputs("printqd: ERROR: Must be run as root!\n", stderr); fflush(stderr); goto finished; } /* Check used command line arguments. */ if (0 != options[0].found) { debug = 1; } /* Run in debug mode. */ if (0 != debug) { set_signal_handlers_and_run_service(); goto finished; } /* PREPARE TO RUN AS DAEMON */ if (0 == dk4dmt_parent_before_fork(printqd_kw[0],pid_file_name,1,syslogf)) { goto finished; } /* Create first background process */ cpid = fork(); if ((pid_t)0 < cpid) { /* In the parent process */ goto finished; } else { if ((pid_t)0 == cpid) { /* In the child process, continue below */ } else { /* Failed to create process */ /* ERROR: fork() failed */ fputs(printqd_kw[0], stderr); fputs(printqd_kw[70], stderr); fflush(stderr); goto finished; } } /* Intermediate process and daemon process should indicate success. */ exval = EXIT_SUCCESS; /* Decouple from terminals. */ if (0 == dk4dmt_intermediate_before_fork(printqd_kw[0], syslogf)) { goto finished; } /* Create daemon background process from within first background process */ cpid = fork(); if ((pid_t)0 < cpid) { /* In the intermediate process */ goto finished; } else { if ((pid_t)0 == cpid) { /* In the daemon process, continue below */ } else { /* Failed to create process */ /* ERROR: fork() failed */ log_1(NULL, 0UL, LOG_ERR, 1, 71); goto finished; } } /* In the daemon process */ if (0 != dk4dmt_daemon_after_fork(printqd_kw[0], pid_file_name, syslogf)) { set_signal_handlers_and_run_service(); } dk4dmt_daemon_end(printqd_kw[0], pid_file_name, syslogf); /* Exit the program. */ finished: exit(exval); return exval; } #else /** Program entry point. @param argc @param argv @return 0 on success, any other value indicates an error. */ int main(int argc, char *argv[]) { fputs("printqd: ERROR: Build requirements violation!\n", stderr); #if DK4_CHAR_SIZE > 1 fputs("Default character size is larger than 1!\n", stderr); #endif #if !((DK4_HAVE_SIGACTION) || (DK4_HAVE_SIGSET)) fputs("Neither sigaction() nor sigset() are available!\n", stderr); #endif #if !(DK4_HAVE_PWD_H) fputs("Include file \"pwd.h\" not found!\n", stderr); #endif #if !(DK4_HAVE_UID_T) fputs("Data type uid_t not available!\n", stderr); #endif #if !(DK4_HAVE_GRP_H) fputs("Include file \"grp.h\" not found!\n", stderr); #endif #if !(DK4_HAVE_GID_T) fputs("Data type gid_t not available!\n", stderr); #endif #if !(DK4_HAVE_MODE_T) fputs("Data type mode_t not available!\n", stderr); #endif #if !(DK4_HAVE_GETPWNAM) fputs("Function getpwnam() is not available!\n", stderr); #endif #if !(DK4_HAVE_GETGRNAM) fputs("Function getgrnam() is not available!\n", stderr); #endif #if !((DK4_HAVE_SETSID) || (DK4_HAVE_SETPGRP)) fputs("Neither setsid() nor setpgrp() are available!\n", stderr); #endif #if DK4_ON_WINDOWS fputs("On Windows platform!\n", stderr); #endif fflush(stderr); exit(EXIT_FAILURE); return EXIT_FAILURE; } #endif