cmogstored.git  about / heads / tags
alternative mogstored implementation for MogileFS
blob 2b20696bd171d4bda0108f2332c05c1d9fd61d96 3200 bytes (raw)
$ git show HEAD: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
153
 
/*
 * 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"
static Hash_table *processes;

struct worker_kill {
	int signal;
	unsigned count;
};

static bool process_cmp(const void *_a, const void *_b)
{
	const struct mog_process *a = _a;
	const struct mog_process *b = _b;

	return a->pid == b->pid;
}

static size_t process_hash(const void *x, size_t tablesize)
{
	const struct mog_process *p = x;

	return p->pid % tablesize;
}

/* needed to make valgrind happy */
__attribute__((destructor)) static void process_atexit(void)
{
	if (processes)
		hash_free(processes);
}

/* call before forking */
void mog_process_init(size_t nr)
{
	if (nr < 3)
		nr = 3;
	processes = hash_initialize(nr, NULL, process_hash, process_cmp, free);
	mog_oom_if_null(processes);
}

void mog_process_reset(void)
{
	assert(processes && "mog_process_init() never called");
	hash_clear(processes);
}

char *mog_process_name(unsigned id)
{
	char *s;
	if (mog_process_is_worker(id))
		return asprintf(&s, "worker[%u]", id) >= 0 ? s : 0;

	switch (id) {
	case MOG_PROC_UNKNOWN: return 0;
	case MOG_PROC_IOSTAT: return strdup("iostat");
	case MOG_PROC_UPGRADE: return strdup("upgrade");
	}

	return asprintf(&s, "BUG[%u]", id) >= 0 ? s : 0;
}

bool mog_process_is_worker(unsigned id)
{
	switch (id) {
	case MOG_PROC_UNKNOWN:
	case MOG_PROC_IOSTAT:
	case MOG_PROC_UPGRADE:
		return false;
	}
	return true;
}

/* hash iterator */
static bool kill_worker(void *ent, void *k)
{
	struct mog_process *p = ent;
	struct worker_kill *wk = k;

	assert(p->id != MOG_PROC_UNKNOWN &&
	      "MOG_PROC_UNKNOWN should not be registered");

	if (!mog_process_is_worker(p->id))
		return true;

	wk->count++;
	if (kill(p->pid, wk->signal) == 0)
		return true;

	/*
	 * ESRCH: race between receiving a signal and waitpid(),
	 * ignore the error but count it, so we'lll know to wait on it.
	 */
	if (errno != ESRCH)
		syslog(LOG_ERR, "could not signal worker[%u] pid=%d: %m",
		       p->id, (int)p->pid);
	return true;
}

/*
 * send signal to each worker process, returns number of processes
 * signalled.  (signal=0 counts workers registered)
 */
size_t mog_kill_each_worker(int signo)
{
	struct worker_kill wk = { .signal = signo, .count = 0 };

	hash_do_for_each(processes, kill_worker, &wk);

	return (size_t)wk.count;
}

/* Registers a process with a given id */
void mog_process_register(pid_t pid, unsigned id)
{
	struct mog_process *p = malloc(sizeof(struct mog_process));

	assert(id != MOG_PROC_UNKNOWN &&
	      "MOG_PROC_UNKNOWN may not be registered");

	if (!p)
		goto err;

	p->pid = pid;
	p->id = id;

	if (hash_insert(processes, p))
		return; /* success */

	PRESERVE_ERRNO(free(p));
err:
	syslog(LOG_ERR, "unable to register PID:%d with id=%u: %m",
		(int)pid, id);
}

/*
 * Call on a pid after a process is reaped, returns the id of the process
 * Returns MOG_PROC_UNKNOWN if the pid was unknown
 */
unsigned mog_process_reaped(pid_t pid)
{
	struct mog_process p = { .pid = pid, .id = MOG_PROC_UNKNOWN };
	struct mog_process *r;

	r = hash_delete(processes, &p);
	if (r) {
		p.id = r->id;
		free(r);
	}
	return p.id;
}

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