%% options copyright owner = Dirk Krause copyright year = 2016-xxxx license = bsd %% module #include "dk4conf.h" #include "dk4types.h" #include #if DK4_HAVE_STDLIB_H #ifndef STDLIB_H_INCLUDED #include #define STDLIB_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_PROCESS_H #ifndef PROCESS_H_INCLUDED #include #define PROCESS_H_INCLUDED 1 #endif #endif /** Requirements to build this program: * Use 8 bit characters, so we can use the directories from dk4inst.c. * Signal handling mechanism. */ #if (DK4_CHAR_SIZE == 1) && ((DK4_HAVE_SIGACTION) || (DK4_HAVE_SIGSET)) #if DK4_HAVE_MATH_H #ifndef MATH_H_INCLUDED #include #define MATH_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_SYS_TYPES_H #ifndef SYS_TYPES_H_INCLUDED #include #define SYS_TYPES_H_INCLUDED 1 #endif #endif #if DK4_HAVE_IO_H #ifndef IO_H_INCLUDED #include #define IO_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_STRING_H #ifndef STRING_H_INCLUDED #include #define STRING_H_INCLUDED 1 #endif #endif #include "dk4inst.h" #include "dk4mpl.h" #include "dk4mem.h" #include "dk4mai8ddu.h" #include "dk4mai8dus.h" #include "dk4str8.h" #include "dk4lprng.h" #include "dk4opt.h" #include "dk4sock.h" #include "dk4vers.h" $!trace-include /** Information from one LPRng accounting line. */ typedef struct { char *username; char *queuename; char *jobtitle; char *pagecount; } lprng_info_t; /** Command line options for program. */ static dk4_option_t options[] = { { { dkT('i'), dkT("interactive"), DK4_OPT_ARG_NONE }, { NULL }, 0}, { { dkT('s'), dkT("socket"), DK4_OPT_ARG_STRING }, { NULL }, 0}, { { dkT('h'), dkT("host"), DK4_OPT_ARG_STRING }, { NULL }, 0}, { { dkT('p'), dkT("port"), DK4_OPT_ARG_UNSIGNED }, { NULL }, 0}, { { dkT('\0'),dkT("help"), DK4_OPT_ARG_NONE}, { NULL }, 0}, { { dkT('\0'),dkT("version"), DK4_OPT_ARG_NONE}, { NULL }, 0}, { { dkT('\0'),dkT("license"), DK4_OPT_ARG_NONE}, { NULL }, 0} }; /** Help text. */ static const char * const help_text[] = { $!text Print quota daemon client printqdc [-i] [-s path] | [-h host -p port] -i Interactive mode instead of default pipe mode. --interactive -s path Path name for local UNIX domain socket. --socket=path -h host Host name of remote host. --host=host -p port Port number at remote host. --port=port --help Show this help text. --version Show version number. --license Show license information. $!end }; /** License conditions text. */ static const char * const license_text[] = { $!text preprocessor This software uses code from the following projects, either directly or as a library: dktools Dirk Krause's tools and libraries. See http://dktools.sourceforge.net/ for more information. DK tools and libraries license ============================== Copyright (c) 2015-2016, Dirk Krause All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Dirk Krause nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. $!end }; /** Version information. */ static const char version_text[] = { DKT_VERSION_C8 }; /** Constant text keywords used by the module. */ static const char * const pqdc_kw[] = { $!string-table # # 0 Newline # \n # # 1 Space # # # 2 Default job name # x@x+1 # # 3 Default job title # Print job # # 4 # -> # # 5 6 # <- " "\n # # 7 ... 10 # printqdc: ERROR: Port number too large!\n ERROR: Port number required!\n ERROR: Host name required!\n # # 11 ... 15 # ERROR: Failed to shut down socket!\n ERROR: Failed to send data!\n ERROR: Failed to open connection to accounting system!\n ERROR: Failed to construct request (would be too long)!\n ERROR: Incomplete information to construct request!\n # # 16 ... 20 # ERROR: Pagecounter not numeric!\n ERROR: Too few arguments for request!\n ERROR: Unknown command keyword!\n ERROR: Missing arguments!\n ERROR: Interrupted by signal!\n # # 21 ... 22 # ERROR: Failed to install signal handlers!\n ERROR: Failed to restore signal handlers!\n # # 23 # ERROR: Failed to initialize socket subsystem!\n $!end }; /** Default socket name. */ static const char def_sock_name[] = { DK4_INST_DIR_VAR "/run/printqd/printqd.sock" }; /** Protocol keywords at start of accounting line. */ static const char * const command_names[] = { $!string-table # # 0 ... 5 LPRng # jobstart jobend start end filestart fileend # # 6 ... 11 printqd # info acct-check acct-start acct-end acct-charge control $!end }; /** Responses from printqd */ static const char * const responses[] = { $!string-table ACCEPT HOLD REMOVE $!end }; /** Data exchange buffer. */ static char inbuf[4096]; /** Copy for inspection. */ static char cpbuf[sizeof(inbuf)]; /** Host name for network connection. */ static char *host_name = NULL; /** Socket file name for local connection. */ static char *sock_name = NULL; /** Number of options. */ static size_t szoptions = sizeof(options)/sizeof(dk4_option_t); /** Normal exit value (when run as pipe). */ static int exval = LPRNG_EXIT_SUCCESS; /** Help (1), version (2), license (4). */ static int hvl = 0; /** Exit value for interactive mode. */ static int iexval = EXIT_FAILURE; /** Flag: Interactive mode (-i option). */ static int intera = 0; /** Flag: EOF was found. */ static int eof_found = 0; /** Flag: Response already found and processed. */ static int found_response = 0; #ifdef SIGPIPE /** Indicator: SIGPIPE signal received. */ static DK4_VOLATILE dk4_sig_atomic_t sig_had_pipe = 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; /** Port number for network connection. */ static unsigned short portno = 0U; /** 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 /** 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(void) { int back = 1; if (0 != sig_read_atomic(&sig_had_term)) { back = 0; } if (0 != sig_read_atomic(&sig_had_int)) { back = 0; } return back; } /** Log one-component message to stderr. @param kwind Index of text in printqdc_kw array. */ static void log_1(size_t kwind) { if (0 != intera) { fputs(pqdc_kw[7], stderr); fputs(pqdc_kw[kwind], stderr); fflush(stderr); } } /** Eat up input to avoid broken pipe in feeder. */ static void eatup_input(void) { size_t rdb; int cc = 1; do { if (0 != can_continue()) { rdb = fread(inbuf, 1, sizeof(inbuf), stdin); if (0 == rdb) { cc = 0; } } else { cc = -1; } } while(1 == cc); } /** Process command line arguments. @param argc Number of command line arguments. @param argv Command line arguments array. @return 1 on success, 0 on error. */ static int cmd_line_args(int argc, char *argv[]) { dkChar *filenames[32]; size_t szfilenames = DK4_SIZEOF(filenames,DK4_PDKCHAR); int back; back = dk4opt_process_argv( options, szoptions, argc, argv, filenames, &szfilenames, 1, 0, NULL ); if (1 == back) { if (0 != options[4].found) { hvl |= 1; } if (0 != options[5].found) { hvl |= 2; } if (0 != options[6].found) { hvl |= 4; } if (0 != options[0].found) { intera = 1; } if (0 != options[1].found) { sock_name = options[1].val.t; } if (0 != options[2].found) { host_name = options[2].val.t; } if (0 != options[3].found) { if ((dk4_um_t)0x0000FFFFUL >= options[3].val.u) { portno = (unsigned short)(options[3].val.u); } else { back = 0; /* ERROR: Port number to large! */ log_1(8); } } if ((NULL != host_name) && (0 == portno)) { back = 0; /* ERROR: Port number required */ log_1(9); } if ((NULL == host_name) && (0 != portno)) { back = 0; /* ERROR: Host name required */ log_1(10); } } else { if (0 != options[0].found) { (void)dk4opt_process_argv( options, szoptions, argc, argv, filenames, &szfilenames, 1, 1, NULL ); } } return back; } /** Data exchange with accounting system. @param pres Flag: Process response, 0=no, 1=acct-check, 2=info. */ static void process_data(int pres) { const char *ep; /* End pointer for text to number conversions */ char *ptr; /* Traverse data line */ dk4_um_t um; /* Number obtained from text */ size_t sl; /* Input buffer string length */ size_t wrb; /* Bytes we still have to write */ dk4_socket_t sock; /* Connection to accounting/quota daemon */ int res; /* Operation result */ int cc; /* Flag: Can continue */ $? "+ process_data %d", pres wrb = sl = strlen(inbuf); /* LOG/SHOW: Request */ if (0 != intera) { $? ". log at start" fputs(pqdc_kw[4], stdout); if (0 < sl) { fputs(inbuf, stdout); if (0 < sl) { if ('\n' != inbuf[sl - 1]) { fputc('\n', stdout); } } else { fputc('\n', stdout); } } fflush(stdout); } if ((0 < sl) && (0 != can_continue())) { $? ". rq size ok" if ((NULL != host_name) && (0 < portno)) { $? ". network" sock = dk4socket_c8_tcp_client_host_port( host_name, portno, 0, 0L, 0L, NULL ); } else { $? ". local" sock = dk4socket_c8_unix_client( ((NULL != sock_name) ? sock_name : def_sock_name), NULL ); } if (INVALID_SOCKET != sock) { $? ". sock" if (0 != can_continue()) { $? ". can continue" res = dk4socket_send(sock, inbuf, &wrb, 0, 0L, 0L, NULL); if ((DK4_SOCKET_RESULT_SUCCESS == res) && (wrb == sl)) { $? ". send" if (0 != can_continue()) { $? ". can continue" res = dk4socket_shutdown(sock, DK4_SOCKET_SHUT_WRITE, NULL); if (DK4_SOCKET_RESULT_SUCCESS == res) { $? ". shutdown" cc = 1; do { if (0 != can_continue()) { $? ". can continue" wrb = sizeof(inbuf); res = dk4socket_recv(sock, inbuf, &wrb, 0, 0L, 0L, NULL); if ((DK4_SOCKET_RESULT_SUCCESS==res)&&(0 != can_continue())) { if (0 < wrb) { $? ". data" if (0 != pres) { $? ". process response" if (sizeof(inbuf) > wrb) { inbuf[wrb] = '\0'; } else { iexval = EXIT_FAILURE; inbuf[sizeof(inbuf)-1] = '\0'; } dk4str8_normalize(inbuf, NULL); /* LOG/SHOW: Response */ if (0 != intera) { fputs(pqdc_kw[5], stdout); fputs(inbuf, stdout); fputs(pqdc_kw[6], stdout); fflush(stdout); } switch (pres) { case 1: { if (0 == found_response) { switch (dk4str8_array_index(responses,inbuf,0)) { case 1: { exval = LPRNG_EXIT_HOLD; } break; case 2: { exval = LPRNG_EXIT_REMOVE; } break; } } found_response = 1; } break; case 2: { if (0 == found_response) { um = (dk4_um_t)0UL; ep = NULL; ptr = dk4str8_start(inbuf, NULL); if (NULL != ptr) { ptr = dk4str8_next(ptr, NULL); if (NULL != ptr) { ptr = dk4str8_next(ptr, NULL); if (NULL != ptr) { ptr = dk4str8_next(ptr, NULL); if (NULL != ptr) { res = dk4ma_input_c8_dec_dk4_um_t( &um, ptr, &ep, 1, NULL ); if (0 == res) { um = (dk4_um_t)0UL; } } } } } if ((dk4_um_t)0UL == um) { exval = LPRNG_EXIT_REMOVE; } } found_response = 1; } break; } } else { $? ". ignore response" } } else { $? ". 0 bytes response" cc = 0; } } else { $? "! recv" iexval = EXIT_FAILURE; cc = -1; } } else { $? "! signal" cc = -1; } } while (1 == cc); } else { $? "! shutdown" iexval = EXIT_FAILURE; /* ERROR: Failed to shut down socket */ log_1(11); } } else { $? "! signal" } } else { $? "! send" iexval = EXIT_FAILURE; /* ERROR: Sending data failed */ log_1(12); } } else { $? "! signal" } dk4socket_close(sock, NULL); } else { $? "! sock" iexval = EXIT_FAILURE; /* ERROR: Failed to open socket */ log_1(13); } } else { $? "! rq size 0" } $? "- process_data" } /** 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 one line received from LPRng. @param cmd Index in command names array. @param args Further arguments in line. */ static void lprng_line(int cmd, char *args) { lprng_info_t li; /* All relevant components of line */ const char *ep = NULL; /* End pointer for numeric conversion */ const char *jt; /* Job title */ dk4_um_t um; /* Text to number conversion result */ int res; /* Operation result */ int ok = 0; /* Flag: Can process line */ $? "+ lprng_line %d \"%s\"", cmd, TR_8STR(args) if (0 != lprng_info_from_line(&li, args)) { switch (cmd) { case 0: { if ((NULL != li.username) && (NULL != li.queuename)) { if (0 != dk4str8_cpy_s(inbuf,sizeof(inbuf),command_names[7], NULL)) { if (0 != dk4str8_cat_s(inbuf,sizeof(inbuf),pqdc_kw[1], NULL)) { if (0 != dk4str8_cat_s(inbuf,sizeof(inbuf),li.queuename, NULL)) { if (0 != dk4str8_cat_s(inbuf,sizeof(inbuf),pqdc_kw[1], NULL)) { if (0 != dk4str8_cat_s(inbuf,sizeof(inbuf),li.username, NULL)) { if (0 != dk4str8_cat_s(inbuf,sizeof(inbuf),pqdc_kw[0], NULL)) { ok = 1; $? ". new request constructed" } } } } } } if (0 != ok) { process_data(1); } else { $? "! failed to construct" iexval = EXIT_FAILURE; /* ERROR: Failed to construct request */ log_1(14); } } else { $? "! incomplete" iexval = EXIT_FAILURE; /* ERROR: Incomplete information */ log_1(15); } } break; default: { if ((NULL != li.username) && (NULL != li.queuename)) { if (NULL != li.pagecount) { res = dk4ma_input_c8_dec_dk4_um_t(&um, li.pagecount, &ep, 1, NULL); if (0 != res) { cmd += 4; if(0 !=dk4str8_cpy_s(inbuf,sizeof(inbuf),command_names[cmd],NULL)) { if (0 != dk4str8_cat_s(inbuf,sizeof(inbuf),pqdc_kw[1], NULL)) { if (0 != dk4str8_cat_s(inbuf,sizeof(inbuf),li.queuename, NULL)) { if (0 != dk4str8_cat_s(inbuf,sizeof(inbuf),pqdc_kw[1], NULL)) { if (0 != dk4str8_cat_s(inbuf,sizeof(inbuf),li.username, NULL)) { if (0 != dk4str8_cat_s(inbuf,sizeof(inbuf),pqdc_kw[1], NULL)) { if (0 != dk4str8_cat_s(inbuf,sizeof(inbuf),li.pagecount, NULL)) { if (0 != dk4str8_cat_s(inbuf,sizeof(inbuf),pqdc_kw[1], NULL)) { if (0 != dk4str8_cat_s(inbuf,sizeof(inbuf),pqdc_kw[2], NULL)) { if (0 != dk4str8_cat_s(inbuf,sizeof(inbuf),pqdc_kw[1], NULL)) { jt = li.jobtitle; if (NULL == jt) { jt = pqdc_kw[3]; } if (0 != dk4str8_cat_s(inbuf, sizeof(inbuf), jt, NULL)) { if (0 != dk4str8_cat_s(inbuf, sizeof(inbuf), pqdc_kw[0], NULL)) { ok = 1; } } } } } } } } } } } } if (0 != ok) { $? ". rq constructed" process_data(0); } else { $? "! failed to construct rq" iexval = EXIT_FAILURE; /* ERROR: Failed to construct request */ log_1(14); } } else { $? "! pc not numeric" iexval = EXIT_FAILURE; /* ERROR: Pagecounter not numeric */ log_1(16); } } else { $? "! incomplete rq" iexval = EXIT_FAILURE; /* ERROR: Incomplete information */ log_1(15); } } else { $? "! incomplete rq" iexval = EXIT_FAILURE; /* ERROR: Incomplete information */ log_1(15); } } break; } } else { iexval = EXIT_FAILURE; } $? "- lprng_line" } /** Process one line containing printqd style data. @param args Arguments text. @param minargs Minimum required number of arguments. @param num3 Flag: Argument 3 must be unsigned number. @param pres Flag: Process response. */ static void printqd_line(char *args, size_t minargs, int num3, int pres) { const char *ep = NULL; /* End of usable data */ char *next; /* Start of arguments */ dk4_um_t um = (dk4_um_t)0UL; /* Numeric conversion result */ size_t i; /* Current argument index */ int res; /* Operation result */ int errnum = 0; /* Flag: Numeric error */ $? "+ printqd_line" dk4str8_normalize(args, NULL); i = 0; $? ". line = \"%s\"", args while(args) { i++; next = dk4str8_next(args, NULL); if ((3 == i) && (0 != num3)) { $? ". check \"%s\"", args res = dk4ma_input_c8_dec_dk4_um_t(&um, args, &ep, 1, NULL); if (0 == res) { errnum = 1; } } args = next; } $? ". i=%u errnum=%d", (unsigned)i, errnum if ((i >= minargs) && (0 == errnum)) { process_data(pres); } else { if (i < minargs) { /* ERROR: Too few arguments */ log_1(17); } if (0 != errnum) { /* ERROR: Page counter not numeric */ log_1(16); } iexval = EXIT_FAILURE; } $? "- printqd_line" } /** Process input from stdin. */ static void process_input(void) { char *args; /* Start of arguments after command */ int cc; /* Flag: Can continue */ int res; /* Operation result for numeric conversions */ $? "+ process_input" cc = 1; do { if (0 != can_continue()) { $? ". can continue" if (fgets(inbuf, sizeof(inbuf), stdin)) { $? ". data" dk4str8_cpy_s(cpbuf, sizeof(cpbuf), inbuf, NULL); dk4str8_delnl(cpbuf); if (NULL != dk4str8_start(cpbuf, NULL)) { if (0 != intera) { $? ". interactive" fputs(cpbuf, stdout); fputc('\n', stdout); fflush(stdout); } args = dk4str8_next(cpbuf, NULL); if (NULL != args) { $? ". arguments" res = dk4str8_array_index(command_names, cpbuf, 0); if (0 != can_continue()) { switch (res) { case 0: { lprng_line(res, args); } break; case 1: { } break; case 2: { } break; case 3: { } break; case 4: { lprng_line(res, args); } break; case 5: { lprng_line(res, args); } break; case 6: { printqd_line(args, 2, 0, 2); } break; case 7: { printqd_line(args, 2, 0, 1); } break; case 8: { printqd_line(args, 4, 1, 0); } break; case 9: { printqd_line(args, 4, 1, 0); } break; case 10: { printqd_line(args, 3, 1, 0); } break; case 11: { printqd_line(args, 1, 0, 0); } break; default: { iexval = EXIT_FAILURE; /* ERROR: Unknown keyword */ log_1(18); } break; } } else { cc = -1; } } else { $? "! no arguments" /* ERROR: No arguments */ log_1(19); iexval = EXIT_FAILURE; } } else { $? ". skip empty line" } } else { $? ". end of data" eof_found = 1; cc = 0; } } else { $? "! aborted" cc = -1; } } while (1 == cc); $? "- process_input" } /** Show a text paragraph on standard output. @param txt Text to show. */ static void show_text(const char * const *txt) { if (NULL != txt) { while (NULL != *txt) { fputs(*(txt++), stdout); fputc('\n', stdout); } fflush(stdout); } } /** Handle --help, --version, --license options. */ static void run_hvl(void) { if (0 != (1 & hvl)) { show_text(help_text); } if (0 != (2 & hvl)) { fputs(version_text, stdout); fputc('\n', stdout); fflush(stdout); } if (0 != (4 & hvl)) { show_text(license_text); } } /** Main program. @param argc Number of command line arguments. @param argv Command line arguments array. @return Exit status code. */ int main(int argc, char *argv[]) { #ifdef DK4_HAVE_SIGACTION #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 dk4_sig_handler_t *oterm = NULL; dk4_sig_handler_t *oint = NULL; #endif #ifdef DK4_HAVE_SIGACTION #ifdef SIGPIPE int spipe = 0; #endif int sint = 0; int sterm = 0; int s_f_i = 0; int s_f_r = 0; #endif int socki = 0; /* 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 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 oint = sigset(SIGINT, sig_handler_int); oterm = sigset(SIGTERM, sig_handler_term); #endif /* Start tracing */ $!trace-init printqdc.deb $? "+ main" /* Process command line arguments */ if (0 == cmd_line_args(argc, argv)) { goto finished; } /* Help, version, license. */ if (0 != hvl) { run_hvl(); goto finished; } /* Bring socket subsystem up. */ if (DK4_SOCKET_RESULT_SUCCESS != dk4socket_up(NULL)) { /* ERROR: Failed to initialize socket subsystem */ log_1(23); goto finished; } socki = 1; iexval = EXIT_SUCCESS; $? ". comman line arguments processed, going to process input" process_input(); finished: /* Eat up remaining input, if any. */ if ((0 == hvl) && (0 == eof_found)) { eatup_input(); } /* Log notification about abort by signal. */ if (0 == can_continue()) { /* LOG: Interrupted by signal */ log_1(20); iexval = EXIT_FAILURE; } /* Release socket subsystem. */ if (0 != socki) { (void)dk4socket_down(NULL); } /* Restore signal handlers */ #if DK4_HAVE_SIGACTION if (0 != s_f_i) { /* ERROR: Failed to install signal handlers */ log_1(21); iexval = 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 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(22); iexval = EXIT_FAILURE; } #else if (NULL != oterm) { sigset(SIGTERM, oterm); } if (NULL != oint ) { sigset(SIGINT, oint ); } #ifdef SIGPIPE if (NULL != opipe) { sigset(SIGPIPE, opipe); } #endif #endif /* Set exit status code depending on interactive mode */ if (0 != intera) { exval = iexval; } /* Finish tracing */ $? "- main %d", exval $!trace-end /* Exit */ exit(exval); return exval; } #else /* if (DK4_CHAR_SIZE == 1) && ((DK4_HAVE_SIGACTION) || (DK4_HAVE_SIGSET)) */ /** Main program. @param argc Number of command line arguments. @param argv Command line arguments array. @return Exit status code. */ int main(int argc, char *argv[]) { fputs("printqdc: ERROR: Program can not be used on this system!\n", stderr); fflush(stderr); exit(EXIT_FAILURE); return EXIT_FAILURE; } #endif /* if (DK4_CHAR_SIZE == 1) && ((DK4_HAVE_SIGACTION) || (DK4_HAVE_SIGSET)) */