cmogstored.git  about / heads / tags
alternative mogstored implementation for MogileFS
blob 90a291f690d664019bdaf880d14b0bc66ae71b94 3562 bytes (raw)
$ git show v1.4.2:iostat_process.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
151
152
 
/*
 * Copyright (C) 2012-2015 all contributors <cmogstored-public@bogomips.org>
 * License: GPLv3 or later (see COPYING for details)
 */
/*
 * process management for iostat(1)
 * Since iostat(1) watches the entire system, we only spawn it once
 * regardless of the number of mog_svc objects we have.
 */
#include "cmogstored.h"

static pid_t iostat_pid;
static time_t iostat_last_fail;
static struct mog_iostat *iostat;
static time_t iostat_fail_timeout = 10;

static void iostat_atexit(void)
{
	if (iostat_pid > 0)
		kill(iostat_pid, SIGTERM);
}

static int iostat_pipe_init(int *fds)
{
	if (pipe2(fds, O_CLOEXEC) < 0) {
		PRESERVE_ERRNO( syslog(LOG_ERR, "pipe2() failed: %m") );

		/*
		 * don't retry here, MFS can deal with not getting iostat
		 * data for a while
		 */
		if (errno == ENFILE || errno == EMFILE)
			PRESERVE_ERRNO( (void)mog_fdmap_expire(5) );
		return -1;
	}

	CHECK(int, 0, mog_set_nonblocking(fds[0], true));
	/* fds[1] (write end) stays _blocking_ */

	return 0;
}

/* only called in the child process */
static const char * exec_cmd(const char *cmd)
{
	time_t last_fail = time(NULL) - iostat_last_fail;
	time_t delay = iostat_fail_timeout - last_fail;

	if (delay <= 0)
		return xasprintf("exec %s", cmd);

	syslog(LOG_DEBUG,
	       "delaying exec of `%s' for %ds due to previous failure",
	       cmd, (int)delay);
	return xasprintf("sleep %d; exec %s", (int)delay, cmd);
}

static void dup2_or_die(int oldfd, int newfd, const char *errdesc)
{
	int rc;

	do
		rc = dup2(oldfd, newfd);
	while (rc < 0 && (errno == EINTR || errno == EBUSY));

	if (rc < 0) {
		syslog(LOG_CRIT, "dup2(%s) failed: %m", errdesc);
		abort();
	}
}

static void preexec_redirect(int out_fd)
{
	int null_fd;

	dup2_or_die(out_fd, STDOUT_FILENO, "iostat_pipe[1],STDOUT");
	mog_close(out_fd);

	null_fd = open("/dev/null", O_RDONLY);
	if (null_fd < 0) {
		syslog(LOG_CRIT, "open(/dev/null) failed: %m");
		abort();
	}
	dup2_or_die(null_fd, STDIN_FILENO, "/dev/null,STDIN");
	mog_close(null_fd);

	/* don't touch stderr */
}

static pid_t iostat_fork_exec(int out_fd)
{
	/* rely on /bin/sh to parse iostat command-line args */
	const char *cmd = getenv("MOG_IOSTAT_CMD");
	if (!cmd)
		cmd = "iostat -dx 1 30";

	cmd = exec_cmd(cmd);

	iostat_pid = fork();
	if (iostat_pid < 0) {
		syslog(LOG_ERR, "fork() for iostat failed: %m");
	} else if (iostat_pid > 0) {
		mog_process_register(iostat_pid, MOG_PROC_IOSTAT);
		mog_close(out_fd);
	} else {
		/* child */
		preexec_redirect(out_fd);
		if (! mog_cloexec_atomic)
			mog_cloexec_from(STDERR_FILENO + 1);

		mog_intr_enable();
		execl("/bin/sh", "sh", "-c", cmd, (char *)NULL);
		syslog(LOG_CRIT, "execl(%s) failed: %m", cmd);
		abort();
	}
	mog_free(cmd);
	return iostat_pid;
}

bool mog_iostat_respawn(int oldstatus)
{
	int fds[2];
	struct mog_fd *mfd;

	if (WIFEXITED(oldstatus) && WEXITSTATUS(oldstatus) == 0) {
		/* syslog(LOG_DEBUG, "iostat done, restarting"); */
	} else {
		iostat_last_fail = time(NULL);
		syslog(LOG_WARNING,
		       "iostat done (pid=%d, status=%d), will retry in %ds",
		       (int)iostat_pid, oldstatus, (int)iostat_fail_timeout);
	}
	iostat_pid = 0;

	if (iostat_pipe_init(fds) < 0)
		return false; /* EMFILE || ENFILE */
	if (iostat_fork_exec(fds[1]) < 0)
		return false; /* fork() failure */

	assert(fds[0] >= 0 && "invalid FD");

	mfd = mog_fd_init(fds[0], MOG_FD_TYPE_IOSTAT);

	if (iostat == NULL)
		atexit(iostat_atexit);
	iostat = &mfd->as.iostat;
	iostat->queue = mog_notify_queue;
	mog_iostat_init(iostat);
	mog_idleq_add(iostat->queue, mfd, MOG_QEV_RD);

	return true;
}

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