LCOV - code coverage report
Current view: top level - source3/printing - samba-bgqd.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 112 151 74.2 %
Date: 2024-04-21 15:09:00 Functions: 4 5 80.0 %

          Line data    Source code
       1             : /*
       2             :  *  Printing background queue helper
       3             :  *
       4             :  *  This program is free software; you can redistribute it and/or modify
       5             :  *  it under the terms of the GNU General Public License as published by
       6             :  *  the Free Software Foundation; either version 3 of the License, or
       7             :  *  (at your option) any later version.
       8             :  *
       9             :  *  This program is distributed in the hope that it will be useful,
      10             :  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             :  *  GNU General Public License for more details.
      13             :  *
      14             :  *  You should have received a copy of the GNU General Public License
      15             :  *  along with this program; if not, see <http://www.gnu.org/licenses/>.
      16             :  */
      17             : 
      18             : #include "replace.h"
      19             : #include "system/filesys.h"
      20             : #include "lib/util/server_id.h"
      21             : #include "source3/locking/share_mode_lock.h"
      22             : #include "source3/param/loadparm.h"
      23             : #include "source3/param/param_proto.h"
      24             : #include "lib/cmdline/cmdline.h"
      25             : #include "lib/cmdline/closefrom_except.h"
      26             : #include "lib/util/talloc_stack.h"
      27             : #include "lib/util/debug.h"
      28             : #include "lib/util/signal.h"
      29             : #include "lib/util/fault.h"
      30             : #include "lib/util/become_daemon.h"
      31             : #include "lib/util/charset/charset.h"
      32             : #include "lib/util/samba_util.h"
      33             : #include "lib/util/sys_rw.h"
      34             : #include "lib/util/pidfile.h"
      35             : #include "lib/async_req/async_sock.h"
      36             : #include "dynconfig/dynconfig.h"
      37             : #include "source3/lib/global_contexts.h"
      38             : #include "messages.h"
      39             : #include "nsswitch/winbind_client.h"
      40             : #include "source3/include/auth.h"
      41             : #include "source3/lib/util_procid.h"
      42             : #include "source3/auth/proto.h"
      43             : #include "source3/printing/queue_process.h"
      44             : #include "source3/lib/substitute.h"
      45             : 
      46          12 : static void watch_handler(struct tevent_req *req)
      47             : {
      48          12 :         bool *pdone = tevent_req_callback_data_void(req);
      49          12 :         *pdone = true;
      50          12 : }
      51             : 
      52           0 : static void bgqd_sig_term_handler(
      53             :         struct tevent_context *ev,
      54             :         struct tevent_signal *se,
      55             :         int signum,
      56             :         int count,
      57             :         void *siginfo,
      58             :         void *private_data)
      59             : {
      60           0 :         bool *pdone = private_data;
      61           0 :         *pdone = true;
      62           0 : }
      63             : 
      64         110 : static bool ready_signal_filter(
      65             :         struct messaging_rec *rec, void *private_data)
      66             : {
      67         110 :         pid_t pid = getpid();
      68             :         ssize_t written;
      69             : 
      70         110 :         if (rec->msg_type != MSG_DAEMON_READY_FD) {
      71          94 :                 return false;
      72             :         }
      73          16 :         if (rec->num_fds != 1) {
      74           0 :                 return false;
      75             :         }
      76             : 
      77          16 :         written = sys_write(rec->fds[0], &pid, sizeof(pid));
      78          16 :         if (written != sizeof(pid)) {
      79           0 :                 DBG_ERR("Could not write pid: %s\n", strerror(errno));
      80             :         }
      81             : 
      82          16 :         return false;
      83             : }
      84             : 
      85          28 : static int samba_bgqd_pidfile_create(
      86             :         struct messaging_context *msg_ctx,
      87             :         const char *progname,
      88             :         int ready_signal_fd)
      89          28 : {
      90          28 :         const char *piddir = lp_pid_directory();
      91          28 :         size_t len = strlen(piddir) + strlen(progname) + 6;
      92          28 :         char pidFile[len];
      93             :         pid_t existing_pid;
      94             :         int fd, ret;
      95             : 
      96          28 :         snprintf(pidFile,
      97             :                  sizeof(pidFile),
      98             :                  "%s/%s.pid",
      99             :                  piddir, progname);
     100             : 
     101          28 :         ret = pidfile_path_create(pidFile, &fd, &existing_pid);
     102          28 :         if (ret == 0) {
     103          12 :                 struct tevent_req *ready_signal_req = NULL;
     104             : 
     105             :                 /*
     106             :                  * Listen for fd's sent via MSG_DAEMON_READY_FD:
     107             :                  * Multiple instances of this process might have raced
     108             :                  * for creating the pidfile. Make sure the parent does
     109             :                  * not suffer from this race, reply on behalf of the
     110             :                  * loser of this race.
     111             :                  */
     112             : 
     113          12 :                 ready_signal_req = messaging_filtered_read_send(
     114             :                         msg_ctx,
     115             :                         messaging_tevent_context(msg_ctx),
     116             :                         msg_ctx,
     117             :                         ready_signal_filter,
     118             :                         NULL);
     119          12 :                 if (ready_signal_req == NULL) {
     120           0 :                         DBG_DEBUG("messaging_filtered_read_send failed\n");
     121           0 :                         pidfile_unlink(piddir, progname);
     122           0 :                         pidfile_fd_close(fd);
     123           0 :                         return ENOMEM;
     124             :                 }
     125             : 
     126             :                 /* leak fd */
     127          12 :                 return 0;
     128             :         }
     129             : 
     130          16 :         if (ret != EAGAIN) {
     131           0 :                 DBG_DEBUG("pidfile_path_create() failed: %s\n",
     132             :                           strerror(ret));
     133           0 :                 return ret;
     134             :         }
     135             : 
     136          16 :         DBG_DEBUG("%s pid %d exists\n", progname, (int)existing_pid);
     137             : 
     138          16 :         if (ready_signal_fd != -1) {
     139             :                 /*
     140             :                  * We lost the race for the pidfile, but someone else
     141             :                  * can report readiness on our behalf.
     142             :                  */
     143          16 :                 NTSTATUS status = messaging_send_iov(
     144             :                         msg_ctx,
     145             :                         pid_to_procid(existing_pid),
     146             :                         MSG_DAEMON_READY_FD,
     147             :                         NULL,
     148             :                         0,
     149             :                         &ready_signal_fd,
     150             :                         1);
     151          16 :                 if (!NT_STATUS_IS_OK(status)) {
     152           0 :                         DBG_DEBUG("Could not send ready_signal_fd: %s\n",
     153             :                                   nt_errstr(status));
     154             :                 }
     155             :         }
     156             : 
     157          16 :         return EAGAIN;
     158             : }
     159             : 
     160          28 : int main(int argc, const char *argv[])
     161             : {
     162          28 :         struct samba_cmdline_daemon_cfg *cmdline_daemon_cfg = NULL;
     163             :         const struct loadparm_substitution *lp_sub =
     164          28 :                 loadparm_s3_global_substitution();
     165          28 :         const char *progname = getprogname();
     166          28 :         TALLOC_CTX *frame = NULL;
     167             :         poptContext pc;
     168          28 :         struct messaging_context *msg_ctx = NULL;
     169          28 :         struct tevent_context *ev = NULL;
     170          28 :         struct tevent_req *watch_req = NULL;
     171          28 :         struct tevent_signal *sigterm_handler = NULL;
     172          28 :         struct bq_state *bq = NULL;
     173          28 :         int log_stdout = 0;
     174          28 :         int ready_signal_fd = -1;
     175          28 :         int watch_fd = -1;
     176             :         NTSTATUS status;
     177             :         int ret;
     178             :         bool ok;
     179          28 :         bool done = false;
     180          28 :         int exitcode = 1;
     181             : 
     182          84 :         struct poptOption long_options[] = {
     183             :                 POPT_AUTOHELP
     184          28 :                 POPT_COMMON_SAMBA
     185          28 :                 POPT_COMMON_DAEMON
     186             : 
     187             :                 /*
     188             :                  * File descriptor to write the PID of the helper
     189             :                  * process to
     190             :                  */
     191             :                 {
     192             :                         .longName   = "ready-signal-fd",
     193             :                         .argInfo    = POPT_ARG_INT,
     194             :                         .arg        = &ready_signal_fd,
     195             :                         .descrip    = "Fd to signal readiness to" ,
     196             :                 },
     197             : 
     198             :                 /*
     199             :                  * Read end of a pipe held open by the parent
     200             :                  * smbd. Exit this process when it becomes readable.
     201             :                  */
     202             :                 {
     203             :                         .longName   = "parent-watch-fd",
     204             :                         .argInfo    = POPT_ARG_INT,
     205             :                         .arg        = &watch_fd,
     206             :                         .descrip    = "Fd to watch for exiting",
     207             :                 },
     208             :                 POPT_TABLEEND
     209             :         };
     210             : 
     211             :         {
     212          28 :                 const char *fd_params[] = {
     213             :                         "ready-signal-fd", "parent-watch-fd",
     214             :                 };
     215             : 
     216          28 :                 closefrom_except_fd_params(
     217             :                         3, ARRAY_SIZE(fd_params), fd_params, argc, argv);
     218             :         }
     219             : 
     220          28 :         talloc_enable_null_tracking();
     221          28 :         frame = talloc_stackframe();
     222          28 :         umask(0);
     223          28 :         set_remote_machine_name("smbd-bgqd", true);
     224             : 
     225          28 :         ok = samba_cmdline_init(frame,
     226             :                                 SAMBA_CMDLINE_CONFIG_SERVER,
     227             :                                 true /* require_smbconf */);
     228          28 :         if (!ok) {
     229           0 :                 DBG_ERR("Failed to setup cmdline parser!\n");
     230           0 :                 exit(ENOMEM);
     231             :         }
     232             : 
     233          28 :         cmdline_daemon_cfg = samba_cmdline_get_daemon_cfg();
     234             : 
     235          28 :         pc = samba_popt_get_context(progname,
     236             :                                     argc,
     237             :                                     argv,
     238             :                                     long_options,
     239             :                                     0);
     240          28 :         if (pc == NULL) {
     241           0 :                 DBG_ERR("Failed to get popt context!\n");
     242           0 :                 exit(ENOMEM);
     243             :         }
     244             : 
     245          28 :         ret = poptGetNextOpt(pc);
     246          28 :         if (ret < -1) {
     247           0 :                 fprintf(stderr, "invalid options: %s\n", poptStrerror(ret));
     248           0 :                 goto done;
     249             :         }
     250             : 
     251          28 :         poptFreeContext(pc);
     252             : 
     253          28 :         log_stdout = (debug_get_log_type() == DEBUG_STDOUT);
     254             : 
     255             :         /* main process will notify systemd */
     256          28 :         daemon_sd_notifications(false);
     257             : 
     258          28 :         if (!cmdline_daemon_cfg->fork) {
     259          28 :                 daemon_status(progname, "Starting process ... ");
     260             :         } else {
     261           0 :                 become_daemon(true,
     262           0 :                               cmdline_daemon_cfg->no_process_group,
     263             :                               log_stdout);
     264             :         }
     265             : 
     266          28 :         BlockSignals(true, SIGPIPE);
     267             : 
     268          28 :         smb_init_locale();
     269          28 :         dump_core_setup(progname, lp_logfile(frame, lp_sub));
     270             : 
     271          28 :         msg_ctx = global_messaging_context();
     272          28 :         if (msg_ctx == NULL) {
     273           0 :                 DBG_ERR("messaging_init() failed\n");
     274           0 :                 goto done;
     275             :         }
     276          28 :         ev = messaging_tevent_context(msg_ctx);
     277             : 
     278          28 :         ret = samba_bgqd_pidfile_create(msg_ctx, progname, ready_signal_fd);
     279          28 :         if (ret != 0) {
     280          16 :                 goto done;
     281             :         }
     282             : 
     283          12 :         if (watch_fd != -1) {
     284          12 :                 watch_req = wait_for_read_send(ev, ev, watch_fd, true);
     285          12 :                 if (watch_req == NULL) {
     286           0 :                         fprintf(stderr, "tevent_add_fd failed\n");
     287           0 :                         goto done;
     288             :                 }
     289          12 :                 tevent_req_set_callback(watch_req, watch_handler, &done);
     290             :         }
     291             : 
     292          12 :         (void)winbind_off();
     293          12 :         ok = init_guest_session_info(frame);
     294          12 :         (void)winbind_on();
     295          12 :         if (!ok) {
     296           0 :                 DBG_ERR("init_guest_session_info failed\n");
     297           0 :                 goto done;
     298             :         }
     299             : 
     300          12 :         (void)winbind_off();
     301          12 :         status = init_system_session_info(frame);
     302          12 :         (void)winbind_on();
     303          12 :         if (!NT_STATUS_IS_OK(status)) {
     304           0 :                 DBG_ERR("init_system_session_info failed: %s\n",
     305             :                         nt_errstr(status));
     306           0 :                 goto done;
     307             :         }
     308             : 
     309          12 :         sigterm_handler = tevent_add_signal(
     310             :                 ev, frame, SIGTERM, 0, bgqd_sig_term_handler, &done);
     311          12 :         if (sigterm_handler == NULL) {
     312           0 :                 DBG_ERR("Could not install SIGTERM handler\n");
     313           0 :                 goto done;
     314             :         }
     315             : 
     316          12 :         bq = register_printing_bq_handlers(frame, msg_ctx);
     317          12 :         if (bq == NULL) {
     318           0 :                 DBG_ERR("Could not register bq handlers\n");
     319           0 :                 goto done;
     320             :         }
     321             : 
     322          12 :         ok = locking_init();
     323          12 :         if (!ok) {
     324           0 :                 DBG_ERR("locking_init failed\n");
     325           0 :                 goto done;
     326             :         }
     327             : 
     328          12 :         if (ready_signal_fd != -1) {
     329          12 :                 pid_t pid = getpid();
     330             :                 ssize_t written;
     331             : 
     332          12 :                 written = sys_write(ready_signal_fd, &pid, sizeof(pid));
     333          12 :                 if (written != sizeof(pid)) {
     334           0 :                         DBG_ERR("Reporting readiness failed\n");
     335           0 :                         goto done;
     336             :                 }
     337          12 :                 close(ready_signal_fd);
     338          12 :                 ready_signal_fd = -1;
     339             :         }
     340             : 
     341        3773 :         while (!done) {
     342        3761 :                 TALLOC_CTX *tmp = talloc_stackframe();
     343        3761 :                 ret = tevent_loop_once(ev);
     344        3761 :                 TALLOC_FREE(tmp);
     345        3761 :                 if (ret != 0) {
     346           0 :                         DBG_ERR("tevent_loop_once failed\n");
     347           0 :                         break;
     348             :                 }
     349             :         }
     350             : 
     351          12 :         exitcode = 0;
     352          28 : done:
     353          28 :         TALLOC_FREE(watch_req);
     354          28 :         TALLOC_FREE(bq);
     355          28 :         TALLOC_FREE(sigterm_handler);
     356          28 :         global_messaging_context_free();
     357          28 :         TALLOC_FREE(frame);
     358          28 :         return exitcode;
     359             : }

Generated by: LCOV version 1.14