cmogstored.git  about / heads / tags
alternative mogstored implementation for MogileFS
blob 32164de74a01a2cf16a5e85d300b4a7b2382cebd 3238 bytes (raw)
$ git show HEAD:upgrade.c	# shows this blob on the CLI

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
 
/*
 * Copyright (C) 2012-2020 all contributors <cmogstored-public@yhbt.net>
 * License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
 */
#include "cmogstored.h"
#include "compat_memstream.h"

static struct {
	char **argv;
	char **envp;
} start;

#define FD_PFX "CMOGSTORED_FD="

MOG_NOINLINE static void free_list(char **head)
{
	char **tmp = head;

	if (tmp) {
		for (; *tmp; tmp++)
			free(*tmp);
		free(head);
	}
}

/* only needed to make valgrind happy */
__attribute__((destructor)) static void upgrade_atexit(void)
{
	free_list(start.argv);
	free_list(start.envp);
}

void mog_upgrade_prepare(int argc, char *argv[], char *envp[])
{
	int i;
	size_t env_count = 2; /* extra for NULL-termination and CMOGSTORED_FD */
	char **e;

	/* duplicate argv */
	start.argv = xmalloc(sizeof(char *) * (argc + 1));
	for (i = 0; i < argc; i++)
		start.argv[i] = xstrdup(argv[i]);
	start.argv[argc] = NULL;

	/* allocate slots for envp */
	for (e = envp; *e; e++)
		env_count++;
	start.envp = xmalloc(sizeof(char *) * env_count);

	/* duplicate envp */
	e = start.envp;
	*e++ = xstrdup(FD_PFX); /* placeholder */
	for (; *envp; envp++) {
		if (strncmp(*envp, FD_PFX, strlen(FD_PFX)))
			*e++ = xstrdup(*envp);
	}
	*e = NULL;
}

/* writes one comma-delimited fd to fp */
static bool emit_fd(FILE *fp, struct mog_fd *mfd)
{
	int r;

	/* no error, just the FD isn't used */
	if (mfd == NULL)
		return true;

	errno = 0;
	r = fprintf(fp, "%d,", mfd->fd);
	if (r > 0)
		return true;
	if (errno == 0)
		errno = ENOSPC;
	syslog(LOG_ERR, "fprintf() failed: %m");
	return false;
}

static bool svc_emit_fd_i(void *svcptr, void *_fp)
{
	FILE *fp = _fp;
	struct mog_svc *svc = svcptr;

	return (emit_fd(fp, svc->mgmt_mfd)
	        && emit_fd(fp, svc->http_mfd)
	        && emit_fd(fp, svc->httpget_mfd));
}

/* returns the PID of the newly spawned child */
pid_t mog_upgrade_spawn(void)
{
	pid_t pid = -1;
	FILE *fp;
	size_t bytes;
	char *dst = NULL;
	int rc;
	const char *execfile;

	if (!mog_pidfile_upgrade_prepare())
		return pid;

	fp = open_memstream(&dst, &bytes);
	if (fp == NULL) {
		syslog(LOG_ERR, "open_memstream failed for upgrade: %m");
		return pid;
	}

	execfile = find_in_path(start.argv[0]);
	errno = 0;
	rc = fputs(FD_PFX, fp);
	if (rc < 0 || rc == EOF) {
		if (errno == 0)
			errno = ferror(fp);
		PRESERVE_ERRNO( (void)fclose(fp) );
		syslog(LOG_ERR, "fputs returned %d on memstream: %m", rc);
		goto out;
	}

	mog_svc_each(svc_emit_fd_i, fp);
	errno = 0;
	if ((my_memstream_close(fp, &dst, &bytes) != 0) && (errno != EINTR)) {
		syslog(LOG_ERR, "fclose on memstream failed for upgrade: %m");
		goto out;
	}

	assert(dst[bytes - 1] == ',' && "not comma-terminated no listeners?");
	dst[bytes - 1] = '\0'; /* kill the last comma */
	start.envp[0] = dst;

	pid = mog_fork_for_exec();
	if (pid == 0) {
		mog_svc_upgrade_prepare();
		execve(execfile, start.argv, start.envp);
		_exit(2);
	} else if (pid > 0) {
		mog_process_register(pid, MOG_PROC_UPGRADE);
		syslog(LOG_INFO, "upgrade spawned PID:%d", pid);
	} else {
		syslog(LOG_ERR, "fork failed for upgrade: %m");
	}

out:
	/* find_in_path does not malloc if output == input */
	if (execfile != start.argv[0])
		mog_free(execfile);
	start.envp[0] = 0;
	free(dst);

	return pid;
}

git clone https://yhbt.net/cmogstored.git