/* * Copyright (c) 2002-2005 Sendmail, Inc. and its suppliers. * All rights reserved. * * By using this file, you agree to the terms and conditions set * forth in the LICENSE file which can be found at the top level of * the sendmail distribution. */ #include "sm/generic.h" SM_RCSID("@(#)$Id: qmgr_start.c,v 1.127 2007/06/18 04:42:31 ca Exp $") #include "sm/error.h" #include "sm/assert.h" #include "sm/qmgr.h" #include "sm/qmgr-int.h" #include "sm/unixsock.h" #include "sm/signal.h" #include "sm/resource.h" #include "sm/misc.h" #include "qmgr.h" #include "log.h" /* ** QM_LISTEN -- start one QMGR listener ** ** Parameters: ** sockname -- path for (Unix domain) socket ** XXX Needs to be fixed for Windows! ** pfd -- (pointer to) fd (output) ** ** Returns: ** usual sm_error code */ static sm_ret_T qm_listen(const char *sockname, int *pfd) { int lfd, r; sm_ret_T ret; SM_REQUIRE(sockname != NULL); SM_REQUIRE(pfd != NULL); ret = SM_SUCCESS; r = unlink(sockname); if (r < 0 && errno != ENOENT) { ret = sm_error_perm(SM_EM_Q_START, errno); sm_io_fprintf(smioerr, "sev=ERROR, func=qm_listen, socket=%s, unlink=%m\n", sockname, ret); goto error; } lfd = unix_server_listen(sockname, 10); if (!is_valid_socket(lfd)) { ret = sm_error_perm(SM_EM_Q_START, errno); sm_io_fprintf(smioerr, "sev=ERROR, func=qm_listen, socket=%s, unix_server_listen=%m\n", sockname, lfd); goto error; } r = chmod(sockname, 0660); if (r < 0) { ret = sm_error_perm(SM_EM_Q_START, errno); sm_io_fprintf(smioerr, "sev=ERROR, func=qm_listen, socket=%s, chmod=%m\n", sockname, ret); goto error; } *pfd = lfd; /* FALLTHROUGH */ error: return ret; } /* ** QM_STLI -- start QMGR listeners ** ** Parameters: ** qmgr_ctx -- QMGR context ** ** Returns: ** usual sm_error code ** ** Question: should we store "task" in qmgr_ctx? */ static sm_ret_T qm_stli(qmgr_ctx_P qmgr_ctx) { int fd; uint u; sm_ret_T ret; sm_evthr_task_P task; SM_IS_QMGR_CTX(qmgr_ctx); /* start listen connections */ /* these should be in qmgr_ctx after the configuration has been read */ /* SMTPS */ ret = qm_listen(qmgr_ctx->qmgr_cnf.q_cnf_smtpssock_abs, &fd); if (sm_is_err(ret)) goto error; ret = evthr_task_new(qmgr_ctx->qmgr_ev_ctx, &task, EVTHR_EV_LI, fd, NULL, qm_gen_li, (void *) &qmgr_ctx->qmgr_ss_li); if (sm_is_err(ret)) goto error; qmgr_ctx->qmgr_ss_li.qm_gli_lfd = fd; /* SMTPC (XXX just for now, later on QMGR requests SMTPC to start) */ ret = qm_listen(qmgr_ctx->qmgr_cnf.q_cnf_smtpcsock_abs, &fd); if (sm_is_err(ret)) goto error; ret = evthr_task_new(qmgr_ctx->qmgr_ev_ctx, &task, EVTHR_EV_LI, fd, NULL, qm_gen_li, (void *) &qmgr_ctx->qmgr_sc_li); if (sm_is_err(ret)) goto error; qmgr_ctx->qmgr_sc_li.qm_gli_lfd = fd; /* control socket */ if (qmgr_ctx->qmgr_cnf.q_cnf_ctlsock_abs != NULL && *qmgr_ctx->qmgr_cnf.q_cnf_ctlsock_abs != '\0') { ret = qm_listen(qmgr_ctx->qmgr_cnf.q_cnf_ctlsock_abs, &fd); if (sm_is_err(ret)) goto error; ret = evthr_task_new(qmgr_ctx->qmgr_ev_ctx, &task, EVTHR_EV_LI, fd, NULL, qm_ctl_li, (void *) qmgr_ctx); if (sm_is_err(ret)) goto error; qmgr_ctx->qmgr_ctllfd = fd; } fd = INVALID_SOCKET; /* simple loop: count seconds, sleep(1) is used */ for (u = 0; u < qmgr_ctx->qmgr_cnf.q_cnf_wait4srv && !is_valid_socket(fd); u++) { #if SMAR_TCP_NET ret = net_client_connect(smarip, smarport, &fd); #else ret = unix_client_connect(qmgr_ctx->qmgr_cnf.q_cnf_smarsock_abs, &fd); #endif if (!is_valid_socket(fd)) sleep(1); else break; } if (!is_valid_socket(fd)) { sm_io_fprintf(smioerr, "sev=ERROR, func=qm_stli, socket=%s, unix_client_connect=%m\n", qmgr_ctx->qmgr_cnf.q_cnf_smarsock_abs, ret); goto error; } qmgr_ctx->qmgr_ar_fd = fd; ret = sm_rcb_open_rcv(qmgr_ctx->qmgr_ar_ctx->qar_com.rcbcom_rdrcb); if (sm_is_err(ret)) { sm_io_fprintf(smioerr, "sev=ERROR, func=qm_stli, sm_rcb_open_rcv=%m\n", ret); goto error; } ret = sm_fd_nonblock(fd, true); if (sm_is_err(ret)) goto error; /* XXX COMPLAIN */ /* start a task for the new AR */ ret = evthr_task_new(qmgr_ctx->qmgr_ev_ctx, &qmgr_ctx->qmgr_ar_tsk, EVTHR_EV_RD, fd, NULL, qmgr_ar, qmgr_ctx->qmgr_ar_ctx); if (sm_is_err(ret)) { sm_io_fprintf(smioerr, "sev=ERROR, func=qm_stli, evthr_task_new=%m\n", ret); goto error; } SM_IS_EVTHR_TSK(qmgr_ctx->qmgr_ar_tsk); qmgr_ctx->qmgr_ar_ctx->qar_com.rcbcom_tsk = qmgr_ctx->qmgr_ar_tsk; if (sm_is_err(ret)) goto error; QM_LEV_DPRINTFC(QDC_START, 6, (QM_DEBFP, "sev=DBG, func=qm_stli, status=OK\n")); qmgr_ctx->qmgr_st_time = evthr_time(qmgr_ctx->qmgr_ev_ctx); return SM_SUCCESS; error: CLOSE_FD(qmgr_ctx->qmgr_ss_li.qm_gli_lfd); CLOSE_FD(qmgr_ctx->qmgr_sc_li.qm_gli_lfd); CLOSE_FD(qmgr_ctx->qmgr_ar_fd); return ret; } /* ** QM_REOPEN_MAPS -- reopen QMGR maps ** ** Parameters: ** qmgr_ctx -- QMGR context ** ** Returns: ** usual sm_error code */ sm_ret_T qm_reopen_maps(qmgr_ctx_P qmgr_ctx) { sm_ret_T ret; SM_IS_QMGR_CTX(qmgr_ctx); /* reload map(s) */ ret = SM_SUCCESS; if (qmgr_ctx->qmgr_conf_map != NULL) { ret = sm_map_reopen(qmgr_ctx->qmgr_conf_map, 0, SMPO_END); if (sm_is_err(ret)) { /* qmgr_ctx->qmgr_conf_map = NULL; */ sm_log_write(qmgr_ctx->qmgr_lctx, QM_LCAT_CONTROL, QM_LMOD_SCHED, SM_LOG_ERR, 2, "sev=ERROR, func=qm_reopen_maps, sm_map_reopen=%m", ret); } } return SM_SUCCESS; } /* ** QM_INFO -- print current usage and configuration ** ** Parameters: ** qmgr_ctx -- QMGR context ** fp -- file pointer for output ** ** Returns: ** usual sm_error code */ sm_ret_T qm_info(qmgr_ctx_P qmgr_ctx, sm_file_T *fp) { int r; uint32_t j; uint u; struct rusage rusage; #if SM_BHT_PERF char out[1024]; #endif SM_IS_QMGR_CTX(qmgr_ctx); if (qmgr_ctx->qmgr_ev_ctx != NULL) { time_T now; now = evthr_time(qmgr_ctx->qmgr_ev_ctx); sm_io_fprintf(fp, "["); (void) sm_timestamp(now, fp); sm_io_fprintf(fp, "] sev=INFO, where=qmgr_usr1\n"); } #if SM_HEAP_CHECK sm_io_fprintf(fp, "sev=INFO, func=qm_info\n"); if (HEAP_CHECK) sm_heap_report(fp, 3); #endif /* configuration */ qmgr_prt_cnf(&qmgr_ctx->qmgr_cnf, fp, true); /* print only active entries? */ for (j = 1, r = 0; r < QM_N_SS_GLI(qmgr_ctx); r++, j *= 2) { qss_ctx_P qss_ctx; if (0 == (qmgr_ctx->qmgr_ss_li.qm_gli_used & j) || (qss_ctx = qmgr_li_ss(qmgr_ctx, r)) == NULL) continue; sm_io_fprintf(fp, "qss_max_thrs [%2d] =%7u\n" "qss_max_cur_thrds[%2d] =%7u\n" "qss_cur_session [%2d] =%7u\n" , r, qss_ctx->qss_max_thrs , r, qss_ctx->qss_max_cur_thrs , r, qss_ctx->qss_cur_session ); } for (r = 0; r < QM_N_SC_GLI(qmgr_ctx); r++) { qsc_ctx_P qsc_ctx; if ((qsc_ctx = qmgr_li_sc(qmgr_ctx, r)) == NULL || qsc_ctx->qsc_dadb_ctx == NULL) continue; #if 0 sm_io_fprintf(fp, "qsc_curactive [%2d] =%7u\n", r, qmgr_ctx->qmgr_scctx[r]->qsc_curactive); sm_io_fprintf(fp, "qsc_maxthreads [%2d] =%7u\n", r, qmgr_ctx->qmgr_scctx[r]->qsc_maxthreads); #else /* 0 */ sm_io_fprintf(fp, "dadb_entries_cur [%2d] =%7u\n", r, qsc_ctx->qsc_dadb_ctx->dadb_entries_cur); sm_io_fprintf(fp, "dadb_entries_idle[%2d] =%7u\n", r, qsc_ctx->qsc_dadb_ctx->dadb_entries_idle); sm_io_fprintf(fp, "dadb_entries_lim [%2d] =%7u\n", r, qsc_ctx->qsc_dadb_ctx->dadb_entries_lim); sm_io_fprintf(fp, "dadb_entries_max [%2d] =%7u\n", r, qsc_ctx->qsc_dadb_ctx->dadb_entries_max); #if DADB_STATS sm_io_fprintf(fp, "dadb_entries_used[%2d] =%7u\n", r, qsc_ctx->qsc_dadb_ctx->dadb_entries_max_used); #endif #endif /* 0 */ } #if QMGR_STATS sm_io_fprintf(fp, "Transactions received=%7lu\n", qmgr_ctx->qmgr_tas_rcvd); sm_io_fprintf(fp, "Recipients received =%7lu\n", qmgr_ctx->qmgr_rcpts_rcvd); sm_io_fprintf(fp, "Transactions sent =%7lu\n", qmgr_ctx->qmgr_tas_sent); sm_io_fprintf(fp, "Recipients sent =%7lu\n", qmgr_ctx->qmgr_rcpts_sent); #endif /* QMGR_STATS */ sm_io_fprintf(fp, "IQDB usage =%7u %%\n", iqdb_usage(qmgr_ctx->qmgr_iqdb)); sm_io_fprintf(fp, "IQDB current =%7u\n", iqdb_entries(qmgr_ctx->qmgr_iqdb, &u)); sm_io_fprintf(fp, "IQDB max_used =%7u\n", u); sm_io_fprintf(fp, "AQ usage (all) =%7u %%\n", aq_usage(qmgr_ctx->qmgr_aq, AQ_USAGE_ALL)); sm_io_fprintf(fp, "AQ usage (defedb) =%7u %%\n", aq_usage(qmgr_ctx->qmgr_aq, AQ_USAGE_DEFEDB)); sm_io_fprintf(fp, "AQ current =%7u\n", qmgr_ctx->qmgr_aq->aq_entries); #if AQ_STATS sm_io_fprintf(fp, "AQ waitq current =%7u\n", qmgr_ctx->qmgr_aq->aq_waitq_entries); sm_io_fprintf(fp, "AQ waitq max_used =%7u\n", qmgr_ctx->qmgr_aq->aq_waitq_max); #endif sm_io_fprintf(fp, "AQ rdq max_used =%7u\n", qmgr_ctx->qmgr_aq->aq_rdq_used_max); sm_io_fprintf(fp, "EDBC current =%7u\n", qmgr_ctx->qmgr_edbc->edbc_entries); sm_io_fprintf(fp, "IBDB kbfree =%7lu\n", qmgr_ctx->qmgr_ibdb_kbfree); sm_io_fprintf(fp, "CDB kbfree =%7lu\n", qmgr_ctx->qmgr_cdb_kbfree); sm_io_fprintf(fp, "EDB kbfree =%7lu\n", qmgr_ctx->qmgr_edb_kbfree); sm_io_fprintf(fp, "qmgr_rflags =%#08x\n", qmgr_ctx->qmgr_rflags); sm_io_fprintf(fp, "qmgr_sflags =%#08x\n", qmgr_ctx->qmgr_sflags); sm_io_fprintf(fp, "qmgr_total_usage =%3u %%\n", qmgr_ctx->qmgr_total_usage); for (u = 0; u <= QMGR_RFL_LAST_I; u++) { sm_io_fprintf(fp, "qmgr_usage[%d] =%3u %%\n", u, qmgr_ctx->qmgr_usage[u]); #if QMGR_STATS sm_io_fprintf(fp, "qmgr_max_use[%d] =%3u %%\n", u, qmgr_ctx->qmgr_max_use[u]); #endif } (void) ibdb_stats(qmgr_ctx->qmgr_ibdb, fp); (void) ibdbc_show_seq(qmgr_ctx->qmgr_ibdb, THR_LOCK_UNLOCK, fp); (void) edb_status(qmgr_ctx->qmgr_edb, fp); #if 0 && SM_BHT_PERF /* which table(s)? */ sm_bht_stats(table, out, sizeof(out)); #endif (void) occ_print(qmgr_ctx->qmgr_occ_ctx, fp, THR_LOCK_UNLOCK); r = getrusage(RUSAGE_SELF, &rusage); if (0 == r) { sm_io_fprintf(fp, "ru_utime= %7ld.%07ld s\n" "ru_stime= %7ld.%07ld s\n" "ru_maxrss= %7ld\n" "ru_ixrss= %7ld\n" "ru_idrss= %7ld\n" "ru_isrss= %7ld\n" "ru_minflt= %7ld\n" "ru_majflt= %7ld\n" "ru_nswap= %7ld\n" "ru_inblock= %7ld\n" "ru_oublock= %7ld\n" "ru_msgsnd= %7ld\n" "ru_msgrcv= %7ld\n" "ru_nsignals=%7ld\n" "ru_nvcsw= %7ld\n" "ru_nivcsw= %7ld\n" , rusage.ru_utime.tv_sec , rusage.ru_utime.tv_usec , rusage.ru_stime.tv_sec , rusage.ru_stime.tv_usec , rusage.ru_maxrss , rusage.ru_ixrss , rusage.ru_idrss , rusage.ru_isrss , rusage.ru_minflt , rusage.ru_majflt , rusage.ru_nswap , rusage.ru_inblock , rusage.ru_oublock , rusage.ru_msgsnd , rusage.ru_msgrcv , rusage.ru_nsignals , rusage.ru_nvcsw , rusage.ru_nivcsw ); } (void) sm_io_flush(fp); return SM_SUCCESS; } /* ** QM_USR1 -- task handler for SIGUSR1 ** print current usage and configuration ** ** Parameters: ** tsk -- evthr task ** ** Returns: ** EVTHR_WAITQ */ static sm_ret_T qm_usr1(sm_evthr_task_P tsk) { qmgr_ctx_P qmgr_ctx; SM_IS_EVTHR_TSK(tsk); qmgr_ctx = (qmgr_ctx_P) tsk->evthr_t_actx; SM_IS_QMGR_CTX(qmgr_ctx); (void) qm_reopen_maps(qmgr_ctx); (void) qm_info(qmgr_ctx, smioout); return EVTHR_WAITQ; } /* ** QM_USR2 -- task handler for SIGUSR2 ** reopen logfiles (useful for log rotation right now, should be ** handled differently later, e.g., see isc log module) ** ** Parameters: ** tsk -- evthr task ** ** Returns: ** EVTHR_WAITQ */ static sm_ret_T qm_usr2(sm_evthr_task_P tsk) { qmgr_ctx_P qmgr_ctx; SM_IS_EVTHR_TSK(tsk); qmgr_ctx = (qmgr_ctx_P) tsk->evthr_t_actx; SM_IS_QMGR_CTX(qmgr_ctx); (void) sm_log_reopen(qmgr_ctx->qmgr_lctx); return EVTHR_WAITQ; } /* ** QMGR_START -- start QMGR ** ** Parameters: ** qmgr_ctx -- QMGR context ** ** Returns: ** usual sm_error code ** ** Last code review: 2003-10-24 17:29:23 */ sm_ret_T qmgr_start(qmgr_ctx_P qmgr_ctx) { sm_ret_T ret; timeval_T now, sleept, delay; sm_evthr_task_P task; ibdb_rcpt_T ibdb_rcpt; sessta_id_T ta_id; SM_IS_QMGR_CTX(qmgr_ctx); sm_memzero(&ibdb_rcpt, sizeof(ibdb_rcpt)); sm_memzero(&ta_id, sizeof(ta_id)); ret = ssocc_open(&qmgr_ctx->qmgr_ssocc_ctx, qmgr_ctx->qmgr_cnf.q_cnf_iqdb_htsize, qmgr_ctx->qmgr_cnf.q_cnf_ss_cc_size); if (sm_is_err(ret)) { sm_log_write(qmgr_ctx->qmgr_lctx, QM_LCAT_INIT, QM_LMOD_START, SM_LOG_ERR, 2, "sev=ERROR, func=qmgr_start, ssocc_open=%m", ret); goto error; } ret = occ_open(&qmgr_ctx->qmgr_occ_ctx, qmgr_ctx->qmgr_cnf.q_cnf_occ_size); if (sm_is_err(ret)) { sm_log_write(qmgr_ctx->qmgr_lctx, QM_LCAT_INIT, QM_LMOD_START, SM_LOG_ERR, 2, "sev=ERROR, func=qmgr_start, occ_open=%m", ret); goto error; } ret = evthr_timeval(qmgr_ctx->qmgr_ev_ctx, &now); if (sm_is_err(ret)) { sm_log_write(qmgr_ctx->qmgr_lctx, QM_LCAT_INIT, QM_LMOD_START, SM_LOG_ERR, 2, "sev=ERROR, func=qmgr_start, evthr_timeval=%m", ret); goto error; } /* XXX is it ok to run this that early? */ ret = qm_edb_scan(qmgr_ctx); if (sm_is_err(ret)) { sm_log_write(qmgr_ctx->qmgr_lctx, QM_LCAT_INIT, QM_LMOD_START, SM_LOG_ERR, 2, "sev=ERROR, func=qmgr_start, qm_edb_scan=%m", ret); goto error; } /* ** Add a dummy record to IBDB; must be added/removed otherwise IBDB ** will complain. ** The dummy record is there to keep track of the latest id counter ** for SMTPS in case QMGR is restarted without receiving a new ** transaction. ** Note: there's a race condition: IBDB is removed in qmgr_init() ** but a new entry is only written here, so the counter can get ** "lost" if the system stops between those two functions. ** However, the startup code checks also DEFEDB for existing ** entries, hence the counter should be at least bigger than ** any counter from a previous run unless there is no data ** in either EDB in which case it is possible that IDs are ** reused which may screw up logfile analyzers... ** The race condition could be closed if another entry is written ** before the old IBDB is removed. */ sm_snprintf(ta_id, sizeof(ta_id), SMTPS_STID_FORMAT, qmgr_ctx->qmgr_idc, 0); ibdb_rcpt.ibr_ta_id = ta_id; ibdb_rcpt.ibr_idx = 0; ibdb_rcpt.ibr_pa = qmgr_ctx->qmgr_pm_addr; ret = ibdb_rcpt_status(qmgr_ctx->qmgr_ibdb, &ibdb_rcpt, IBDB_RCPT_NEW, IBDB_FL_NONE, THR_LOCK_UNLOCK); if (sm_is_err(ret)) { sm_log_write(qmgr_ctx->qmgr_lctx, QM_LCAT_INIT, QM_LMOD_START, SM_LOG_ERR, 2, "sev=ERROR, func=qmgr_start, ibdb_rcpt_status1=%m", ret); goto error; } ret = ibdb_rcpt_status(qmgr_ctx->qmgr_ibdb, &ibdb_rcpt, IBDB_RCPT_DONE, IBDB_FL_NONE, THR_LOCK_UNLOCK); if (sm_is_err(ret)) { sm_log_write(qmgr_ctx->qmgr_lctx, QM_LCAT_INIT, QM_LMOD_START, SM_LOG_ERR, 2, "sev=ERROR, func=qmgr_start, ibdb_rcpt_status2=%m", ret); goto error; } ret = ibdb_commit(qmgr_ctx->qmgr_ibdb); if (sm_is_err(ret)) { sm_log_write(qmgr_ctx->qmgr_lctx, QM_LCAT_INIT, QM_LMOD_START, SM_LOG_ERR, 2, "sev=ERROR, func=qmgr_start, ibdb_commit=%m", ret); goto error; } NOTE(COMPETING_THREADS_NOW) ret = qm_stli(qmgr_ctx); if (sm_is_err(ret)) { sm_log_write(qmgr_ctx->qmgr_lctx, QM_LCAT_INIT, QM_LMOD_START, SM_LOG_ERR, 2, "sev=ERROR, func=qmgr_start, qm_stli=%m", ret); goto error; } /* ** XXX Notice: on a quiet system it is not a good idea to have the ** commit thread wake up every second just to figure out that ** it got nothing to do, see also qmgr_ibdb_commit. */ /* initial timeout: 1s */ sleept = now; sleept.tv_sec++; ret = evthr_task_new(qmgr_ctx->qmgr_ev_ctx, &qmgr_ctx->qmgr_icommit, EVTHR_EV_SL, INVALID_FD, &sleept, qmgr_ibdb_commit, qmgr_ctx); if (sm_is_err(ret)) { sm_log_write(qmgr_ctx->qmgr_lctx, QM_LCAT_INIT, QM_LMOD_START, SM_LOG_ERR, 2, "sev=ERROR, func=qmgr_start, start_qmgr_ibdb_commit=%m", ret); goto error; } sleept = now; /* initial timeout: 10s */ delay.tv_usec = 0; delay.tv_sec = 10; timeradd(&now, &delay, &(sleept)); ret = evthr_task_new(qmgr_ctx->qmgr_ev_ctx, &qmgr_ctx->qmgr_sched, EVTHR_EV_SL, INVALID_FD, &sleept, qmgr_sched, qmgr_ctx); if (sm_is_err(ret)) { sm_log_write(qmgr_ctx->qmgr_lctx, QM_LCAT_INIT, QM_LMOD_START, SM_LOG_ERR, 2, "sev=ERROR, func=qmgr_start, start_qmgr_sched=%m", ret); goto error; } sleept = now; sleept.tv_sec += 60; ret = qcleanup_ctx_new(qmgr_ctx, &qmgr_ctx->qmgr_cleanup_ctx); if (sm_is_err(ret)) { sm_log_write(qmgr_ctx->qmgr_lctx, QM_LCAT_INIT, QM_LMOD_START, SM_LOG_ERR, 2, "sev=ERROR, func=qmgr_start, qcleanup_ctx_new=%m", ret); goto error; } ret = evthr_task_new(qmgr_ctx->qmgr_ev_ctx, &qmgr_ctx->qmgr_tsk_cleanup, EVTHR_EV_SL, INVALID_FD, &sleept, qmgr_cleanup, qmgr_ctx->qmgr_cleanup_ctx); if (sm_is_err(ret)) { sm_log_write(qmgr_ctx->qmgr_lctx, QM_LCAT_INIT, QM_LMOD_START, SM_LOG_ERR, 2, "sev=ERROR, func=qmgr_start, start_qmgr_cleanup=%m", ret); goto error; } ret = evthr_task_new(qmgr_ctx->qmgr_ev_ctx, &task, EVTHR_EV_SG, SIGUSR1, NULL, qm_usr1, (void *) qmgr_ctx); if (sm_is_err(ret)) { sm_log_write(qmgr_ctx->qmgr_lctx, QM_LCAT_INIT, QM_LMOD_START, SM_LOG_ERR, 2, "sev=ERROR, func=qmgr_start, start_sm_qm_usr1=%m", ret); goto error; } ret = evthr_task_new(qmgr_ctx->qmgr_ev_ctx, &task, EVTHR_EV_SG, SIGUSR2, NULL, qm_usr2, (void *) qmgr_ctx); if (sm_is_err(ret)) { sm_log_write(qmgr_ctx->qmgr_lctx, QM_LCAT_INIT, QM_LMOD_START, SM_LOG_ERR, 2, "sev=ERROR, func=qmgr_start, start_sm_qm_usr2=%m", ret); goto error; } #if QMGR_TEST delay.tv_usec = 0; delay.tv_sec = 1; timeradd(&now, &delay, &sleept); (void) evthr_new_sl(qmgr_ctx->qmgr_sched, sleept, false); #endif /* QMGR_TEST */ qmgr_ctx->qmgr_status = QMGR_ST_START; return SM_SUCCESS; error: /* ** cleanup? tasks may have been started, but QMGR will terminate ** due to the errors. */ return ret; }