cmogstored 1.8.1 - use default system stack size
[cmogstored.git] / upgrade.c
blob32164de74a01a2cf16a5e85d300b4a7b2382cebd
1 /*
2 * Copyright (C) 2012-2020 all contributors <cmogstored-public@yhbt.net>
3 * License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
4 */
5 #include "cmogstored.h"
6 #include "compat_memstream.h"
8 static struct {
9 char **argv;
10 char **envp;
11 } start;
13 #define FD_PFX "CMOGSTORED_FD="
15 MOG_NOINLINE static void free_list(char **head)
17 char **tmp = head;
19 if (tmp) {
20 for (; *tmp; tmp++)
21 free(*tmp);
22 free(head);
26 /* only needed to make valgrind happy */
27 __attribute__((destructor)) static void upgrade_atexit(void)
29 free_list(start.argv);
30 free_list(start.envp);
33 void mog_upgrade_prepare(int argc, char *argv[], char *envp[])
35 int i;
36 size_t env_count = 2; /* extra for NULL-termination and CMOGSTORED_FD */
37 char **e;
39 /* duplicate argv */
40 start.argv = xmalloc(sizeof(char *) * (argc + 1));
41 for (i = 0; i < argc; i++)
42 start.argv[i] = xstrdup(argv[i]);
43 start.argv[argc] = NULL;
45 /* allocate slots for envp */
46 for (e = envp; *e; e++)
47 env_count++;
48 start.envp = xmalloc(sizeof(char *) * env_count);
50 /* duplicate envp */
51 e = start.envp;
52 *e++ = xstrdup(FD_PFX); /* placeholder */
53 for (; *envp; envp++) {
54 if (strncmp(*envp, FD_PFX, strlen(FD_PFX)))
55 *e++ = xstrdup(*envp);
57 *e = NULL;
60 /* writes one comma-delimited fd to fp */
61 static bool emit_fd(FILE *fp, struct mog_fd *mfd)
63 int r;
65 /* no error, just the FD isn't used */
66 if (mfd == NULL)
67 return true;
69 errno = 0;
70 r = fprintf(fp, "%d,", mfd->fd);
71 if (r > 0)
72 return true;
73 if (errno == 0)
74 errno = ENOSPC;
75 syslog(LOG_ERR, "fprintf() failed: %m");
76 return false;
79 static bool svc_emit_fd_i(void *svcptr, void *_fp)
81 FILE *fp = _fp;
82 struct mog_svc *svc = svcptr;
84 return (emit_fd(fp, svc->mgmt_mfd)
85 && emit_fd(fp, svc->http_mfd)
86 && emit_fd(fp, svc->httpget_mfd));
89 /* returns the PID of the newly spawned child */
90 pid_t mog_upgrade_spawn(void)
92 pid_t pid = -1;
93 FILE *fp;
94 size_t bytes;
95 char *dst = NULL;
96 int rc;
97 const char *execfile;
99 if (!mog_pidfile_upgrade_prepare())
100 return pid;
102 fp = open_memstream(&dst, &bytes);
103 if (fp == NULL) {
104 syslog(LOG_ERR, "open_memstream failed for upgrade: %m");
105 return pid;
108 execfile = find_in_path(start.argv[0]);
109 errno = 0;
110 rc = fputs(FD_PFX, fp);
111 if (rc < 0 || rc == EOF) {
112 if (errno == 0)
113 errno = ferror(fp);
114 PRESERVE_ERRNO( (void)fclose(fp) );
115 syslog(LOG_ERR, "fputs returned %d on memstream: %m", rc);
116 goto out;
119 mog_svc_each(svc_emit_fd_i, fp);
120 errno = 0;
121 if ((my_memstream_close(fp, &dst, &bytes) != 0) && (errno != EINTR)) {
122 syslog(LOG_ERR, "fclose on memstream failed for upgrade: %m");
123 goto out;
126 assert(dst[bytes - 1] == ',' && "not comma-terminated no listeners?");
127 dst[bytes - 1] = '\0'; /* kill the last comma */
128 start.envp[0] = dst;
130 pid = mog_fork_for_exec();
131 if (pid == 0) {
132 mog_svc_upgrade_prepare();
133 execve(execfile, start.argv, start.envp);
134 _exit(2);
135 } else if (pid > 0) {
136 mog_process_register(pid, MOG_PROC_UPGRADE);
137 syslog(LOG_INFO, "upgrade spawned PID:%d", pid);
138 } else {
139 syslog(LOG_ERR, "fork failed for upgrade: %m");
142 out:
143 /* find_in_path does not malloc if output == input */
144 if (execfile != start.argv[0])
145 mog_free(execfile);
146 start.envp[0] = 0;
147 free(dst);
149 return pid;