%% options copyright owner = Dirk Krause copyright year = 2012-2014 license = bsd %% header /** Preprocessor directive to write statistics. */ #define DK3_PRINTQDI_STATISTICS 1 #include "dk3all.h" #if DK3_CHAR_SIZE == 1 #if DK3_HAVE_PTHREAD_H #include #if DK3_HAVE_STDLIB_H #include #endif #if DK3_HAVE_UNISTD_H #include #endif #include #include "printqd.h" /** Per-thread structure for printqdi. */ typedef struct { char usn[109]; /**< UNIX domain socket name. */ pthread_mutex_t mux; /**< Mutex to protect access to rs. */ pthread_t thr; /**< Thread ID. */ dk3_socket_t sock; /**< Session socket. */ int rs; /**< Run state: 1=running, 0=finished. */ } pqdi_thread_t; /** Job structure for printqdi. */ typedef struct { pthread_attr_t tha; /**< Default thread attributes. */ pthread_mutexattr_t mxa; /**< Default mutex attributes. */ pqd_conf_t conf; /**< Configuration. */ dk3_app_t *app; /**< Application structure. */ dkChar const * const *lm; /**< Localized message texts. */ dkChar const * const *clm; /**< Localized message texts for conf. */ dk3_sto_t *sth; /**< Storage for thread structures. */ dk3_sto_it_t *ith; /**< Iterator through threads storage. */ int cco; /**< Flag: Can continue outer loop. */ } pqdi_job_t; #endif #endif %% module #include "dk3all.h" #if DK3_CHAR_SIZE == 1 #if DK3_HAVE_PTHREAD_H #include "printqdi.h" $!trace-include #if DK3_PRINTQDI_STATISTICS /** Number of thread data structures deleted. */ static unsigned long pqdi_threads_created = 0UL; /** Number of thread data structures destroyed. */ static unsigned long pqdi_threads_destroyed = 0UL; /** Number of threads successfully started. */ static unsigned long pqdi_threads_started = 0UL; #endif /** Constant keywords used by the module, not localized. */ static char const * const pqdi_kw[] = { $!string-table # # 0 Application group name # dkt-3 # # 1 Message texts file. # printqdi.str # # 2 Keyword info starts request line. # info # # 3 Keyword for syslog. # printqdi $!end }; /** Localized message texts. */ static char const * const pqdi_loc[] = { $!string-table file=printqdi.str # # 0 # Failed to initialize default thread attributes! # # 1 # Failed to set default thread state to detached! # # 2 # Failed to initialize default mutex attributes! # # 3 # Failed to set default mutex type! # # 4 # Exiting due to previous error! # # 5 # Exiting normally (due to termination signal). # # 6 # Failed to read configuration file! # # 7 # Configuration problem, configuration check failed! # # 8 # Failed to create listener socket set! # # 9 # Failed to create thread for connection! # # 10 # Failed to switch user account to non-root! # # 11 # Failed to switch group to non-root! # # 12 # Failed to find configuration file name! # # 13 # Failed to set up signal handling! $!end }; /** Current working directory when program is started. */ static char pqdi_cwd[DK3_MAX_PATH]; /** Full configuration file name. */ static char pqdi_config_file[DK3_MAX_PATH]; /** Short configuration file name. */ static char const pqdi_confname[] = { PQD_CONFFILE }; /** Read volatile variable. @param vp Pointer to volatile variable. @return Variable contents. */ static dk3_sig_atomic_t pqdi_read_vol_var(DK3_VOLATILE dk3_sig_atomic_t *vp) { return *vp; } /** Pass through pointer to volatile variable. @param vp Pointer to volatile variable. @return Pointe vp. */ static DK3_VOLATILE dk3_sig_atomic_t * pqdi_pass_vol_ptr(DK3_VOLATILE dk3_sig_atomic_t *vp) { return vp; } /** Flag: SIGINT signal was found. */ static DK3_VOLATILE dk3_sig_atomic_t pqdi_sigint_received = 0; /** Handler for signal SIGINT. @param signo Signal number. */ static void pqdi_sigint_handler(int signo) { #if 0 pqdi_sigint_received = 1; #else *pqdi_pass_vol_ptr(&pqdi_sigint_received) = 1; #endif } /** Flag: SIGTERM signal was found. */ static DK3_VOLATILE dk3_sig_atomic_t pqdi_sigterm_received = 0; /** Handler for signal SIGTERM. @param signo Signal number. */ static void pqdi_sigterm_handler(int signo) { #if 0 pqdi_sigterm_received = 1; #else *pqdi_pass_vol_ptr(&pqdi_sigterm_received) = 1; #endif } /** Flag: SIGPIPE signal was found. */ static DK3_VOLATILE dk3_sig_atomic_t pqdi_sigpipe_received = 0; /** Handler for signal SIGPIPE. @param signo Signal number. */ static void pqdi_sigpipe_handler(int signo) { #if 0 pqdi_sigpipe_received = 1; #else *pqdi_pass_vol_ptr(&pqdi_sigpipe_received) = 1; #endif } /** Flag: SIGHUP signal received. */ static DK3_VOLATILE dk3_sig_atomic_t pqdi_sighup_received = 0; /** Handler for signal SIGHUP. @param signo Signal number. */ static void pqdi_sighup_handler(int signo) { #if 0 pqdi_sighup_received = 1; #else *pqdi_pass_vol_ptr(&pqdi_sighup_received) = 1; #endif } /** Delete a thread structure. @param pth Thread structure to delete. @param clfd Flag: Close socket file descriptor. @param delmux Flag: Destroy mutex. */ static void pqdi_thread_delete(pqdi_thread_t *pth, int clfd, int delmux) { if(delmux) { pthread_mutex_destroy(&(pth->mux)); } if(clfd) { if(INVALID_SOCKET != pth->sock) { dk3socket_close(pth->sock, NULL, NULL); pth->sock = INVALID_SOCKET; } } #if DK3_PRINTQDI_STATISTICS pqdi_threads_destroyed += 1UL; #endif dk3_delete(pth); } /** Create new thread data structure. @param job Job structure. @return Pointer to new structure on success, NULL on error. */ static pqdi_thread_t * pqdi_thread_new(pqdi_job_t *job) { pqdi_thread_t *back = NULL; back = dk3_new_app(pqdi_thread_t,1,job->app); if(back) { #if DK3_PRINTQDI_STATISTICS pqdi_threads_created += 1UL; #endif if(0 == pthread_mutex_init(&(back->mux), &(job->mxa))) { back->sock = INVALID_SOCKET; back->rs = 0; } else { dk3_delete(back); back = NULL; } } return back; } /** Check whether we can continue in outer loop. We leave the outer loop on SIGINT, SIGTERM or unrecoverable errors. @param job Job structure. @return 1 to continue, 0 to exit loop. */ static int pqdi_cc_outer(pqdi_job_t *job) { int back = 1; if(pqdi_read_vol_var(&pqdi_sigterm_received)) { back = 0; } if(pqdi_read_vol_var(&pqdi_sigint_received)) { back = 0; } if(job) { if(!(job->cco)) { back = 0; } } return back; } /** Check whether we can continue in inner loop. We leave the outer loop on SIGINT, SIGTERM, SIGHUP (to reconfigure) or unrecoverable errors. @param job Job structure. @return 1 to continue, 0 to exit loop. */ static int pqdi_cc_inner(pqdi_job_t *job) { int back; back = pqdi_cc_outer(job); if(pqdi_read_vol_var(&pqdi_sighup_received)) { back = 0; } return back; } /** Initialize job structure. @param job Structure to initialize. */ static void pqdi_job_init(pqdi_job_t *job) { dk3mem_res((void *)job, sizeof(pqdi_job_t)); job->cco = 1; } /** Clean up job structure. @param job Job structure to clean up. */ static void pqdi_job_end(pqdi_job_t *job) { if(job->app) { dk3app_close(job->app); job->app = NULL; } } /** Construct configuration file name in pqdi_config_file. @param job Job structure. @return 1 on success, 0 on error. */ static int pqdi_construct_config_file_name(pqdi_job_t *job) { char const * const *xargv; /* Command line arguments array. */ char const *scd; /* System configuration directory. */ int xargc; /* Number of command line arguments. */ int back = 0; pqdi_config_file[0] = '\0'; xargc = dk3app_get_argc(job->app); xargv = dk3app_get_argv(job->app); if(xargc > 1) { strcpy(pqdi_config_file, pqdi_cwd); back = dk3str_c8_append_path_app( pqdi_config_file, sizeof(pqdi_config_file), xargv[1], job->app ); } else { scd = dk3inst_get_directory(1); if(scd) { if((strlen(scd)+strlen(pqdi_confname)) < sizeof(pqdi_config_file)) { strcpy(pqdi_config_file, scd); strcat(pqdi_config_file, pqdi_confname); back = 1; } } } if(!(back)) { /* ERROR: Failed to find configuration file name! */ dk3app_log_1(job->app, DK3_LL_ERROR, job->lm, 12); } return back; } /** Cleanup after leaving outer loop. Stop all threads still running, delete the thread structures. @param job Job structure. */ static void pqdi_cleanup_outer(pqdi_job_t *job) { pqdi_thread_t *pth; unsigned long ul1; unsigned long ul2; int rs; ul1 = ul2 = 0UL; dk3sto_it_reset(job->ith); while(NULL != (pth = (pqdi_thread_t *)dk3sto_it_next(job->ith))) { if(0 == pthread_mutex_lock(&(pth->mux))) { rs = pth->rs; pthread_mutex_unlock(&(pth->mux)); if(rs) { pthread_cancel(pth->thr); ul1++; } } } dk3sto_it_reset(job->ith); while(NULL != (pth = (pqdi_thread_t *)dk3sto_it_next(job->ith))) { pqdi_thread_delete(pth, 1, 1); ul2++; } #if DK3_PRINTQDI_STATISTICS #if DK3_HAVE_SYSLOG openlog(pqdi_kw[3], LOG_PID, LOG_LPR); syslog(LOG_INFO, "Cancelled %lu threads at end, deleted %lu.", ul1, ul2); closelog(); #endif #endif } /** Change user and group before entering service the first time. @param job Job structure. */ static void pqdi_change_user_and_group(pqdi_job_t *job) { if(((job->conf).uid) || ((job->conf).gid)) { if(dk3app_change_user(job->app, (job->conf).uid, (job->conf).gid)) { if((job->conf).gid) { if(0 != setgid((job->conf).gid)) { /* ERROR: Failed to switch group! */ dk3app_log_1(job->app, DK3_LL_ERROR, job->lm, 11); job->cco = 0; } } if((job->conf).uid) { if(0 != setuid((job->conf).uid)) { /* ERROR: Failed to switch user! */ dk3app_log_1(job->app, DK3_LL_ERROR, job->lm, 10); job->cco = 0; } } } else { /* ERROR: Failed to change user for application! */ dk3app_log_1(job->app, DK3_LL_ERROR, job->lm, 10); job->cco = 0; } } } /** The function running as separated thread. Read request from network, connect to UNIX domain socket, send request, receive response and send response back over network. @param threadarg The thread data structure. @return NULL. */ static void * pqdi_thread_function(void *threadarg) { char bu[PQD_INPUT_BUFFER_SIZE]; /* Input buffer. */ char b2[PQD_INPUT_BUFFER_SIZE]; /* Test buffer. */ char bo[PQD_OUTPUT_BUFFER_SIZE]; /* Output buffer. */ char o2[PQD_OUTPUT_BUFFER_SIZE]; /* Real output. */ char *p1; /* info keyword. */ char *p2; /* Queue name. */ char *p3; /* User name. */ char *p4; /* Check for further. */ pqdi_thread_t *pt; /* Thread data. */ dk3_socket_t ss; /* Session socket. */ size_t sl; /* String length. */ int rb; /* Bytes read. */ int first; /* First response. */ pt = (pqdi_thread_t *)threadarg; rb = dk3socket_recv(pt->sock, bu, (sizeof(bu) - 1), 0L, 0L, NULL, NULL); if(rb > 0) { bu[rb] = '\0'; strcpy(b2, bu); p1 = dk3str_c8_start(b2, NULL); if(p1) { p2 = dk3str_c8_next(p1, NULL); if(p2) { p3 = dk3str_c8_next(p2, NULL); if(p3) { p4 = dk3str_c8_next(p3, NULL); if((!(p4)) && (0 == strcmp(p1,pqdi_kw[2]))) { p1 = dk3str_c8_start(bu, NULL); if(p1) { sl = strlen(p1); ss = dk3socket_un_stream_client(pt->usn, 0L, 0L, NULL, NULL); first = 1; if(INVALID_SOCKET != ss) { rb = dk3socket_send(ss, p1, sl, 0L, 0L, NULL, NULL); if((size_t)rb == sl) { if(dk3socket_shutdown(ss,DK3_TCPIP_SHUTDOWN_WRITE,NULL,NULL)) { do { rb = dk3socket_recv( ss, bo, (sizeof(bo)-1), 0L, 0L, NULL, NULL ); if(rb > 0) { if(first) { first = 0; bo[rb] = '\0'; strcpy(o2, bo); } } } while(0 < rb); } } dk3socket_close(ss, NULL, NULL); if(!(first)) { sl = strlen(o2); dk3socket_send(pt->sock, o2, sl, 0L, 0L, NULL, NULL); } } } } } } } } dk3socket_close(pt->sock, NULL, NULL); /* Indicate that we are done. */ if(0 == pthread_mutex_lock(&(pt->mux))) { pt->rs = 0; pt->sock = INVALID_SOCKET; pthread_mutex_unlock(&(pt->mux)); } return NULL; } /** Run inner sevice loop. @param job Job structure. */ static void pqdi_run_inner_loop(pqdi_job_t *job) { #if DK3_HAVE_STRUCT_SOCKADDR_STORAGE struct sockaddr_storage pa; /* Peer address. */ #else struct sockaddr_in pa; /* Peer address. */ #endif fd_set rfds; /* Listener socket test set. */ dk3_socket_t *sptr; /* Socket pointer. */ dk3_socket_set_t *liso; /* Listener sockets. */ pqdi_thread_t *pt; /* Thread structure. */ dk3_socket_t max; /* Maximum socket number. */ dk3_socket_t ns; /* New socket. */ size_t i; /* Current socket index. */ size_t pal; /* Peer address length. */ int allow; /* Flag: Peer allowed to connect. */ $? "+ pqdi_run_inner_loop" liso = dk3socket_listeners((job->conf).port,(job->conf).ibl,0, NULL,job->app); if(liso) { $? ". listener ok" while(pqdi_cc_inner(job)) { $? ". loop start" max = 0; FD_ZERO(&rfds); sptr = liso->pData; for(i = 0; i < liso->szUsed; i++) { if(INVALID_SOCKET != *sptr) { if(*sptr > max) { max = *sptr; } FD_SET(*sptr,&rfds); } sptr++; } $? ". max %d", max if(0 < select((max + 1), &rfds, NULL, NULL, NULL)) { $? ". select" sptr = liso->pData; for(i = 0; ((i < liso->szUsed) && (pqdi_cc_inner(job))); i++) { if(FD_ISSET(*sptr,&rfds)) { $? ". ready %d", *sptr pal = sizeof(pa); ns = dk3socket_accept( *sptr, (struct sockaddr *)(&pa), &pal, NULL, job->app ); if(INVALID_SOCKET != ns) { $? ". accept" allow = 1; if(((job->conf).s_i) && ((job->conf).i_i)) { allow = 0; if(dk3sto_it_find_like((job->conf).i_i, (void *)(&pa), 1)) { allow = 1; } } if(allow) { $? ". allow" pt = pqdi_thread_new(job); if(pt) { $? ". pt" pt->rs = 1; pt->sock = ns; strcpy(pt->usn, (job->conf).usn); if(dk3sto_add(job->sth, (void *)pt)) { $? ". add" allow = pthread_create( &(pt->thr), &(job->tha), pqdi_thread_function, (void *)pt ); if(0 == allow) { $? ". thread" #if DK3_PRINTQDI_STATISTICS pqdi_threads_started += 1UL; #endif } else { $? "! thread" /* ERROR: Failed to create thread! */ dk3app_log_1(job->app, DK3_LL_ERROR, job->lm, 9); dk3sto_remove(job->sth, (void *)pt); pqdi_thread_delete(pt, 1, 1); job->cco = 0; } } else { $? "! add" /* ERROR: Memory! */ dk3app_log_i1(job->app, DK3_LL_ERROR, 9); pqdi_thread_delete(pt, 1, 1); job->cco = 0; } } else { $? "! pt" /* ERROR: Failed to allocate thread data! */ dk3app_log_i1(job->app, DK3_LL_ERROR, 9); dk3socket_close(ns, NULL, NULL); job->cco = 0; } } else { $? "! allow" dk3socket_close(ns, NULL, NULL); } } else { $? "! accept" } } sptr++; } } else { $? "! select" /* No file found */ } /* Release data used by finished threads. */ dk3sto_it_reset(job->ith); while(NULL != (pt = (pqdi_thread_t *)dk3sto_it_next(job->ith))) { if(0 == pthread_mutex_lock(&(pt->mux))) { allow = pt->rs; pthread_mutex_unlock(&(pt->mux)); if(0 == pt->rs) { dk3sto_remove(job->sth, (void *)pt); pqdi_thread_delete(pt, 1, 1); } } } $? ". loop end" } dk3socket_set_close(liso, NULL, job->app); } else { $? "! listener" /* ERROR: Failed to create listener socket set! */ dk3app_log_1(job->app, DK3_LL_ERROR, job->lm, 8); job->cco = 0; } $? "- pqdi_run_inner_loop" } static void pqdi_loops_after_signal_setup(pqdi_job_t *job) { int firstrun = 1; /* Flag: Run first pass. */ int res; /* Result from configuration check. */ $? "+ pqdi_loops_after_signal_setup" job->sth = dk3sto_open_app(job->app); if(job->sth) { job->ith = dk3sto_it_open(job->sth); if(job->ith) { while(pqdi_cc_outer(job)) { res = pqdconf_read( &(job->conf),pqdi_config_file,PQD_PROGRAM_TYPE_INFO,job->app,job->clm ); if(res) { res = pqdconf_check( &(job->conf), PQD_PROGRAM_TYPE_INFO, job->app, job->clm ); if(res) { #if 0 pqdi_sighup_received = 0; #else *pqdi_pass_vol_ptr(&pqdi_sighup_received) = 0; #endif if(firstrun) { firstrun = 0; pqdi_change_user_and_group(job); } pqdi_run_inner_loop(job); } else { /* ERROR: Configuration problem! */ dk3app_log_1(job->app, DK3_LL_ERROR, job->lm, 7); job->cco = 0; } } else { /* ERROR: Failed to read configuration */ dk3app_log_1(job->app, DK3_LL_ERROR, job->lm, 6); job->cco = 0; } pqdconf_cleanup(&(job->conf)); } pqdi_cleanup_outer(job); if(job->cco) { /* INFO Exiting normally. */ dk3app_log_1(job->app, DK3_LL_INFO, job->lm, 5); } else { /* INFO Exiting due to error. */ dk3app_log_1(job->app, DK3_LL_ERROR, job->lm, 4); } #if DK3_HAVE_SYSLOG openlog(pqdi_kw[3], LOG_PID, LOG_LPR); if(job->cco) { syslog(LOG_INFO, "Printqdi daemon exits normally (signal)."); } else { syslog(LOG_INFO, "Printqdi daemon exits due to error!"); } #if DK3_PRINTQDI_STATISTICS syslog( LOG_INFO, "Created %lu, destroyed %lu, started %lu", pqdi_threads_created, pqdi_threads_destroyed, pqdi_threads_started ); #endif closelog(); #endif } else { $? "! memory" /* ERROR: Memory */ dk3app_log_i1(job->app, DK3_LL_ERROR, 9); #if DK3_HAVE_SYSLOG openlog(pqdi_kw[3], LOG_PID, LOG_LPR); syslog(LOG_INFO, "Printqdi failed to start: Not enough memory!"); closelog(); #endif } } else { $? "! memory" /* ERROR: Memory */ dk3app_log_i1(job->app, DK3_LL_ERROR, 9); #if DK3_HAVE_SYSLOG openlog(pqdi_kw[3], LOG_PID, LOG_LPR); syslog(LOG_INFO, "Printqdi failed to start: Not enough memory!"); closelog(); #endif } $? "- pqdi_loops_after_signal_setup" } /** Run outer loop. @param job Job structure. */ static void pqdi_run_loops(pqdi_job_t *job) { #if DK3_HAVE_SIGACTION struct sigaction sanewhup; /* Install new handler for HUP */ struct sigaction sanewint; /* Install new handler for INT */ struct sigaction sanewterm; /* Install new handler for TERM */ struct sigaction sanewpipe; /* Install new handler for PIPE */ struct sigaction sabckhup; /* Save setup for HUP */ struct sigaction sabckint; /* Save setup for INT */ struct sigaction sabckterm; /* Save setup for TERM */ struct sigaction sabckpipe; /* Save setup for PIPE */ int sigok = 0; /* Flag: Signal setup succeeded */ sanewhup.sa_handler = pqdi_sighup_handler; sanewint.sa_handler = pqdi_sigint_handler; sanewterm.sa_handler = pqdi_sigterm_handler; sanewpipe.sa_handler = pqdi_sigpipe_handler; sanewhup.sa_flags = 0; sanewint.sa_flags = 0; sanewterm.sa_flags = 0; sanewpipe.sa_flags = 0; if (sigemptyset(&(sanewhup.sa_mask)) == 0) { if (sigaddset(&(sanewhup.sa_mask), SIGHUP) == 0) { if (sigaction(SIGHUP, &sanewhup, &sabckhup) == 0) { if (sigemptyset(&(sanewint.sa_mask)) == 0) { if (sigaddset(&(sanewint.sa_mask), SIGINT) == 0) { if (sigaction(SIGINT, &sanewint, &sabckint) == 0) { if (sigemptyset(&(sanewterm.sa_mask)) == 0) { if (sigaddset(&(sanewterm.sa_mask), SIGTERM) == 0) { if (sigaction(SIGTERM, &sanewterm, &sabckterm) == 0) { if (sigemptyset(&(sanewpipe.sa_mask)) == 0) { if (sigaddset(&(sanewpipe.sa_mask), SIGPIPE) == 0) { if (sigaction(SIGPIPE, &sanewpipe, &sabckpipe) == 0) { /* Signal handling initialized successfully. */ sigok = 1; /* Run real work. */ pqdi_loops_after_signal_setup(job); /* Restore previous signal handling. */ (void)sigaction(SIGPIPE, &sabckpipe, NULL); } } } (void)sigaction(SIGTERM, &sabckterm, NULL); } } } (void)sigaction(SIGINT, &sabckint, NULL); } } } (void)sigaction(SIGHUP, &sabckhup, NULL); } } } if (0 == sigok) { dk3app_log_1(job->app, DK3_LL_ERROR, job->lm, 13); } #else dk3_signal_disp_t disp_int; /* Original disposition for SIGINT. */ dk3_signal_disp_t disp_hup; /* Original disposition for SIGHUP. */ dk3_signal_disp_t disp_term; /* Original disposition for SIGTERM. */ dk3_signal_disp_t disp_pipe; /* Original disposition for SIGPIPE. */ /* Install signal handlers. */ disp_int = sigset(SIGINT, pqdi_sigint_handler); disp_hup = sigset(SIGHUP, pqdi_sighup_handler); disp_term = sigset(SIGTERM, pqdi_sigterm_handler); disp_pipe = sigset(SIGPIPE, pqdi_sigpipe_handler); /* Run service loops. */ pqdi_loops_after_signal_setup(job); /* Restore previous signal handlers. */ if(disp_pipe) { sigset(SIGPIPE, disp_pipe); } if(disp_term) { sigset(SIGTERM, disp_term); } if(disp_hup) { sigset(SIGHUP, disp_hup); } if(disp_int) { sigset(SIGINT, disp_int); } #endif } /** Run the service in background. @param argc Number of command line arguments. @param argv Command line arguments array. */ static void pqdi_run_background_process(int argc, char const * const *argv) { pqdi_job_t job; $!trace-init /tmp/printqdi.deb $?"+ pqdi_run_background_process" pqdi_job_init(&job); job.app = dk3app_open_daemon(pqdi_cwd, argc, argv, pqdi_kw[0]); if(job.app) { job.lm = dk3app_messages(job.app, pqdi_kw[1], pqdi_loc); job.clm = pqdconf_get_messages(job.app); if(0 == pthread_attr_init(&(job.tha))) { if(0 == pthread_attr_setdetachstate(&(job.tha), PTHREAD_CREATE_DETACHED)) { if(0 == pthread_mutexattr_init(&(job.mxa))) { if(0 == pthread_mutexattr_settype(&(job.mxa), PTHREAD_MUTEX_NORMAL)) { if(pqdi_construct_config_file_name(&job)) { pqdi_run_loops(&job); } } else { /* ERROR: Failed to set default mutex type! */ dk3app_log_1(job.app, DK3_LL_ERROR, job.lm, 3); #if DK3_HAVE_SYSLOG openlog(pqdi_kw[3], LOG_PID, LOG_LPR); syslog( LOG_INFO, "Printqdi failed to start: Failed to set default mutex type!" ); closelog(); #endif } pthread_mutexattr_destroy(&(job.mxa)); } else { /* ERROR: Failed to initialize default mutex attributes! */ dk3app_log_1(job.app, DK3_LL_ERROR, job.lm, 2); #if DK3_HAVE_SYSLOG openlog(pqdi_kw[3], LOG_PID, LOG_LPR); syslog(LOG_INFO, "Printqdi failed to start: Failed to initialize mutex attributes!" ); closelog(); #endif } } else { /* ERROR: Failed to set detached state! */ dk3app_log_1(job.app, DK3_LL_ERROR, job.lm, 1); #if DK3_HAVE_SYSLOG openlog(pqdi_kw[3], LOG_PID, LOG_LPR); syslog( LOG_INFO, "Printqdi failed to start: Failed to set default thread state!" ); closelog(); #endif } pthread_attr_destroy(&(job.tha)); } else { /* ERROR: Failed to initialize default thread attribute set! */ dk3app_log_1(job.app, DK3_LL_ERROR, job.lm, 0); #if DK3_HAVE_SYSLOG openlog(pqdi_kw[3], LOG_PID, LOG_LPR); syslog( LOG_INFO, "Printqdi failed to start: Failed to initialize thread attributes!" ); closelog(); #endif } } else { /* ERROR: Failed to initialize application! */ #if DK3_HAVE_SYSLOG openlog(pqdi_kw[3], LOG_PID, LOG_LPR); syslog( LOG_INFO, "Printqdi failed to start: Failed to initialize application!" ); closelog(); #endif } pqdi_job_end(&job); $? "- pqdi_run_background_process" $!trace-end } /** Program entry point. @param argc Number of command line arguments. @param argv Command line arguments array. @return 0 on success, any other value indicates an error. */ DK3_MAIN { pid_t pid; /* Process ID from fork(). */ int max; /* Maximum number of files. */ int i; /* Current file to close. */ int exval = 1; if(dk3sf_c8_getcwd_app(pqdi_cwd, sizeof(pqdi_cwd), NULL)) { if(0 == chdir("/")) { umask(077); max = dk3sf_get_max_files(); for(i = 0; i < max; i++) { (void)close(i); } pid = fork(); switch(pid) { case 0: { #if DK3_HAVE_SETSID setsid(); #else #if DK3_HAVE_SETPGRP setpgrp(); #else #error "No function to disconnect from parent process!" #endif #endif pqdi_run_background_process(argc, (char const * const *)argv); } break; case -1: { fputs( "printqdi: ERROR: Failed to create new process!\n", stderr ); fflush(stderr); } break; default: { exval = 0; } break; } } else { fputs( "printqdi: ERROR: Failed to change into / directory!\n", stderr ); fflush(stderr); } } else { fputs( "printqdi: ERROR: Failed to find current working directory!\n", stderr ); fflush(stderr); } exit(exval); return exval; } #else int main(int argc, char *argv[]) { fputs("ERROR: POSIX threads not available!\n", stderr); fflush(stderr); } #endif #else int main(int argc, char *argv[]) { fputs("ERROR: Program compiled with wrong character size!\n", stderr); fflush(stderr); } #endif