Blame view

control.c 9.35 KB
redmine authored
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/*
    clsync - file tree sync utility based on fanotify and inotify
    
    Copyright (C) 2013  Dmitry Yu Okunev <dyokunev@ut.mephi.ru> 0x8E30679C
    
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "common.h"

#include <sys/un.h>	// for "struct sockaddr_un"
redmine authored
23 24 25 26
#include <sys/stat.h>	// mkdir()
#include <sys/types.h>	// mkdir()
#include <fcntl.h>	// mkdirat()
#include <glib.h>	// g_hash_table_foreach()
redmine authored
27

redmine authored
28 29 30

#include "indexes.h"
#include "ctx.h"
redmine authored
31
#include "error.h"
redmine authored
32 33
#include "sync.h"
#include "control.h"
redmine authored
34
#include "socket.h"
redmine authored
35 36 37

static pthread_t pthread_control;

redmine authored
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 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212

static inline int control_error(clsyncsock_t *clsyncsock_p, const char *const funct, const char *const args) {
	return socket_send(clsyncsock_p, SOCKCMD_REPLY_ECUSTOM, funct, args, errno, strerror(errno));
}

enum dump_dirfd_obj {
	DUMP_DIRFD_ROOT = 0,
	DUMP_DIRFD_QUEUE,
	DUMP_DIRFD_THREAD,

	DUMP_DIRFD_MAX
};

enum dump_ltype {
	DUMP_LTYPE_INCLUDE,
	DUMP_LTYPE_EXCLUDE,
	DUMP_LTYPE_EVINFO,
};

struct control_dump_arg {
	clsyncsock_t 	*clsyncsock_p;
	ctx_t 		*ctx_p;
	int 		 dirfd[DUMP_DIRFD_MAX];
	int		 fd_out;
	int		 data;
};

void control_dump_liststep(gpointer fpath_gp, gpointer evinfo_gp, gpointer arg_gp) {
	char *fpath			=         (char *)fpath_gp;
	eventinfo_t *evinfo		=  (eventinfo_t *)evinfo_gp;
	struct control_dump_arg *arg 	= 		  arg_gp;
	char act, num;

	switch (arg->data) {
		case DUMP_LTYPE_INCLUDE:
			act = '+';
			num = '1';
			break;
		case DUMP_LTYPE_EXCLUDE:
			act = '-';
			num = '1';
			break;
		case DUMP_LTYPE_EVINFO:
			act = '+';
			num = 	 evinfo->flags&EVIF_RECURSIVELY        ? '*' : 
				(evinfo->flags&EVIF_CONTENTRECURSIVELY ? '/' : '1');
			break;
		default:
			act = '?';
			num = '?';
	}

	dprintf(arg->fd_out, "%c%c %s\n", act, num, fpath);

	return;
}

int control_dump_thread(threadinfo_t *threadinfo_p, void *_arg) {
	struct control_dump_arg *arg = _arg;
	char buf[BUFSIZ];

	snprintf(buf, BUFSIZ, "%u-%u-%lx", threadinfo_p->iteration, threadinfo_p->thread_num, (long)threadinfo_p->pthread);

	arg->fd_out = openat(arg->dirfd[DUMP_DIRFD_THREAD], buf, O_WRONLY);
	if (arg->fd_out == -1)
		return errno;

	{
		char **argv;

		dprintf(arg->fd_out, 
			"thread:\n\titeration == %u;\n\tnum == %u;\n\tpthread == %lx;\n\tstarttime == %lu\n\texpiretime == %lu\n\tchild_pid == %u\n\ttry_n == %u\nCommand:",
				threadinfo_p->iteration,
				threadinfo_p->thread_num,
				(long)threadinfo_p->pthread,
				threadinfo_p->starttime,
				threadinfo_p->expiretime,
				threadinfo_p->child_pid,
				threadinfo_p->try_n
			);

		argv = threadinfo_p->argv;
		while (argv != NULL)
			dprintf(arg->fd_out, " \"%s\"", *(argv++));

		dprintf(arg->fd_out, "\n");
	}

	arg->data = DUMP_LTYPE_EVINFO;
	g_hash_table_foreach(threadinfo_p->fpath2ei_ht, control_dump_liststep, arg);

	close(arg->fd_out);

	return 0;
}

int control_mkdir_open(clsyncsock_t *clsyncsock_p, const char *const dir_path) {
	int dirfd;

	if (mkdir(dir_path, DUMP_DIRMODE)) {
		control_error(clsyncsock_p, "mkdir", dir_path);
		return -1;
	}

	dirfd = open(dir_path, O_RDWR);
	if (dirfd == -1) {
		control_error(clsyncsock_p, "open",  dir_path);
		return -1;
	}

	return dirfd;
}

int control_dump(ctx_t *ctx_p, clsyncsock_t *clsyncsock_p, sockcmd_t *sockcmd_p) {
	indexes_t	*indexes_p	= ctx_p->indexes_p;
	sockcmd_dat_dump_t *dat		= sockcmd_p->data;
	int rootfd;
	struct control_dump_arg arg;
	enum dump_dirfd_obj dirfd_obj;

	static const char *const subdirs[] = {
		[DUMP_DIRFD_QUEUE]	= "queue",
		[DUMP_DIRFD_THREAD]	= "threads"
	};

	rootfd = control_mkdir_open(clsyncsock_p, dat->dir_path);
	if (rootfd == -1)
		goto l_control_dump_end;

	arg.dirfd[DUMP_DIRFD_ROOT] = rootfd;

	dirfd_obj = DUMP_DIRFD_ROOT+1;
	while (dirfd_obj < DUMP_DIRFD_MAX) {
		const char *const subdir = subdirs[dirfd_obj];

		arg.dirfd[dirfd_obj] = control_mkdir_open(clsyncsock_p, subdir);
		if (arg.dirfd[dirfd_obj] == -1)
			goto l_control_dump_end;

		dirfd_obj++;
	}

	arg.clsyncsock_p = clsyncsock_p;
	arg.ctx_p	 = ctx_p;

	int queue_id = 0;
	while (queue_id < QUEUE_MAX) {
		char buf[BUFSIZ];
		snprintf(buf, BUFSIZ, "%u", queue_id);

		arg.fd_out = openat(arg.dirfd[DUMP_DIRFD_QUEUE], buf, O_WRONLY);

		arg.data = DUMP_LTYPE_EVINFO;
		g_hash_table_foreach(indexes_p->fpath2ei_coll_ht[queue_id],  control_dump_liststep, &arg);
		arg.data = DUMP_LTYPE_EXCLUDE;
		g_hash_table_foreach(indexes_p->exc_fpath_coll_ht[queue_id], control_dump_liststep, &arg);

		close(arg.fd_out);
		queue_id++;
	}

	threads_foreach(control_dump_thread, STATE_RUNNING, &arg);

l_control_dump_end:
	dirfd_obj = DUMP_DIRFD_ROOT;
	while (dirfd_obj < DUMP_DIRFD_MAX) {
		if (arg.dirfd[dirfd_obj] != -1)
			close(arg.dirfd[dirfd_obj]);
		dirfd_obj++;
	}

	return errno ? errno : socket_send(clsyncsock_p, SOCKCMD_REPLY_DUMP);
}


redmine authored
213
int control_procclsyncsock(socket_sockthreaddata_t *arg, sockcmd_t *sockcmd_p) {
redmine authored
214 215
	clsyncsock_t	*clsyncsock_p =          arg->clsyncsock_p;
	ctx_t		*ctx_p        = (ctx_t *)arg->arg;
redmine authored
216 217

	switch(sockcmd_p->cmd_id) {
redmine authored
218 219 220 221
		case SOCKCMD_REQUEST_DUMP:
			control_dump(ctx_p, clsyncsock_p, sockcmd_p);
			break;
		case SOCKCMD_REQUEST_INFO:
redmine authored
222
			socket_send(clsyncsock_p, SOCKCMD_REPLY_INFO, ctx_p->config_block, ctx_p->label, ctx_p->flags, ctx_p->flags_set);
redmine authored
223
			break;
redmine authored
224
		case SOCKCMD_REQUEST_DIE:
redmine authored
225 226
			sync_term(SIGTERM);
			break;
redmine authored
227 228
		default:
			return EINVAL;
redmine authored
229 230
	}

redmine authored
231
	return 0;
redmine authored
232 233
}

redmine authored
234 235 236 237
static inline void closecontrol(ctx_t *ctx_p) {
	if(ctx_p->socket) {
		close(ctx_p->socket);
		ctx_p->socket = 0;
redmine authored
238 239 240
	}
}

redmine authored
241
int control_loop(ctx_t *ctx_p) {
redmine authored
242 243 244

	// Starting

redmine authored
245
	debug(1, "started (ctx_p->socket == %u)", ctx_p->socket);
redmine authored
246 247
	int s;

redmine authored
248
	while((s=ctx_p->socket)) {
redmine authored
249 250 251

		// Check if the socket is still alive
		if(socket_check_bysock(s)) {
redmine authored
252
			debug(1, "Control socket closed [case 0]: %s", strerror(errno));
redmine authored
253
			closecontrol(ctx_p);
redmine authored
254 255 256 257
			continue;
		}

		// Waiting for event
redmine authored
258
		debug(3, "waiting for events on the socket");
redmine authored
259 260 261 262 263 264 265 266
		fd_set rfds;

		FD_ZERO(&rfds);
		FD_SET(s, &rfds);

		int count = select(s+1, &rfds, NULL, NULL, NULL);

		// Processing the events
redmine authored
267
		debug(2, "got %i events with select()", count);
redmine authored
268 269 270

		// Processing the events: checks
		if(count == 0) {
redmine authored
271
			debug(2, "select() timed out.");
redmine authored
272 273 274 275
			continue;
		}

		if(count < 0) {
redmine authored
276
			debug(1, "Got negative events count. Closing the socket.");
redmine authored
277
			closecontrol(ctx_p);
redmine authored
278 279 280 281
			continue;
		}

		if(!FD_ISSET(s, &rfds)) {
redmine authored
282
			error("Got event, but not on the control socket. Closing the socket (cannot use \"select()\").");
redmine authored
283
			closecontrol(ctx_p);
redmine authored
284 285 286
			continue;
		}

redmine authored
287
		// Processing the events: accepting new clsyncsock
redmine authored
288

redmine authored
289 290
		clsyncsock_t *clsyncsock_p = socket_accept(s);
		if(clsyncsock_p == NULL) {
redmine authored
291 292 293 294 295

			if(errno == EUSERS)	// Too many connections. Just ignoring the new one.
				continue;

			// Got unknown error. Closing control socket just in case.
redmine authored
296
			error("Cannot socket_accept()");
redmine authored
297
			closecontrol(ctx_p);
redmine authored
298 299 300
			continue;
		}

redmine authored
301
		debug(2, "Starting new thread for new connection.");
redmine authored
302
		socket_sockthreaddata_t *threaddata_p = socket_thread_attach(clsyncsock_p);
redmine authored
303

redmine authored
304
		if (threaddata_p == NULL) {
redmine authored
305
			error("Cannot create a thread for connection");
redmine authored
306
			closecontrol(ctx_p);
redmine authored
307 308 309
			continue;
		}

redmine authored
310 311
		threaddata_p->procfunct		=  control_procclsyncsock;
		threaddata_p->clsyncsock_p	=  clsyncsock_p;
redmine authored
312 313 314
		threaddata_p->arg		=  ctx_p;
		threaddata_p->running		= &ctx_p->socket;
		threaddata_p->authtype		=  ctx_p->flags[SOCKETAUTH];
redmine authored
315
		threaddata_p->flags		=  0;
redmine authored
316

redmine authored
317
		if (socket_thread_start(threaddata_p)) {
redmine authored
318
			error("Cannot start a thread for connection");
redmine authored
319
			closecontrol(ctx_p);
redmine authored
320 321 322
			continue;
		}
#ifdef DEBUG
redmine authored
323
		// To prevent too often connections
redmine authored
324 325 326 327 328 329
		sleep(1);
#endif
	}

	// Cleanup

redmine authored
330
	debug(1, "control_loop() finished");
redmine authored
331 332 333
	return 0;
}

redmine authored
334 335
int control_run(ctx_t *ctx_p) {
	if(ctx_p->socketpath != NULL) {
redmine authored
336 337
		int ret =  0;
		int s   = -1;
redmine authored
338 339

		// initializing clsync-socket subsystem
redmine authored
340
		if ((ret = socket_init()))
redmine authored
341
			error("Cannot init clsync-sockets subsystem.");
redmine authored
342 343


redmine authored
344
		if (!ret) {
redmine authored
345
			clsyncsock_t *clsyncsock = socket_listen_unix(ctx_p->socketpath);
redmine authored
346
			if (clsyncsock == NULL) {
redmine authored
347
				ret = errno;
redmine authored
348 349 350 351
			} else {
				s = clsyncsock->sock;
				socket_cleanup(clsyncsock);
			}
redmine authored
352 353 354
		}

		// fixing privileges
redmine authored
355
		if (!ret) {
redmine authored
356 357
			if(ctx_p->flags[SOCKETMOD])
				if(chmod(ctx_p->socketpath, ctx_p->socketmod)) {
redmine authored
358
					error("Error, Cannot chmod(\"%s\", %o)", 
redmine authored
359
						ctx_p->socketpath, ctx_p->socketmod);
redmine authored
360 361
					ret = errno;
				}
redmine authored
362 363
			if(ctx_p->flags[SOCKETOWN])
				if(chown(ctx_p->socketpath, ctx_p->socketuid, ctx_p->socketgid)) {
redmine authored
364
					error("Error, Cannot chown(\"%s\", %u, %u)", 
redmine authored
365
						ctx_p->socketpath, ctx_p->socketuid, ctx_p->socketgid);
redmine authored
366 367 368 369 370
					ret = errno;
				}
		}

		// finish
redmine authored
371
		if (ret) {
redmine authored
372 373 374 375
			close(s);
			return ret;
		}

redmine authored
376
		ctx_p->socket = s;
redmine authored
377

redmine authored
378
		debug(2, "ctx_p->socket = %u", ctx_p->socket);
redmine authored
379

redmine authored
380
		ret = pthread_create(&pthread_control, NULL, (void *(*)(void *))control_loop, ctx_p);
redmine authored
381 382 383 384 385
	}
	
	return 0;
}

redmine authored
386 387 388 389
int control_cleanup(ctx_t *ctx_p) {
	if(ctx_p->socketpath != NULL) {
		unlink(ctx_p->socketpath);
		closecontrol(ctx_p);
redmine authored
390 391 392 393 394 395 396
		// TODO: kill pthread_control and join
//		pthread_join(pthread_control, NULL);
		socket_deinit();
	}
	return 0;
}