diff options
| author | Sven Moritz Hallberg <sm@khjk.org> | 2008-02-15 18:36:18 +0100 | 
|---|---|---|
| committer | Sven Moritz Hallberg <sm@khjk.org> | 2008-02-15 18:36:18 +0100 | 
| commit | 27db43361a3fdd3420b12aa5bf151dce4545273f (patch) | |
| tree | 66aa8628b758b27a6c600270cd4060654d05f26c | |
| parent | 522a00f1b1163cedf15a86329c0097601eb7940b (diff) | |
implement background keygen via child process
| -rw-r--r-- | irc.c | 2 | ||||
| -rw-r--r-- | irc.h | 6 | ||||
| -rw-r--r-- | otr.c | 173 | 
3 files changed, 153 insertions, 28 deletions
| @@ -135,6 +135,8 @@ irc_t *irc_new( int fd )  	irc->otr_us = otrl_userstate_create();  	irc->otr_keygen = 0; +	irc->otr_to = NULL; +	irc->otr_from = NULL;  	irc->otr_ntodo = 0;  	return( irc ); @@ -98,8 +98,10 @@ typedef struct irc  	gint ping_source_id;  	OtrlUserState otr_us; -	pid_t otr_keygen; -	int otr_ntodo; +	pid_t otr_keygen;    /* pid of keygen slave */ +	FILE *otr_to;        /* pipe to keygen slave */ +	FILE *otr_from;      /* pipe from keygen slave */ +	int otr_ntodo;       /* number of keys left to generate */  } irc_t;  #include "user.h" @@ -42,6 +42,7 @@  #include "irc.h"  #include "otr.h"  #include <sys/types.h> +#include <sys/wait.h>  #include <unistd.h> @@ -105,9 +106,18 @@ const command_t otr_commands[] = {  /* start background process to generate a (new) key for a given account */  void otr_keygen(irc_t *irc, const char *handle, const char *protocol); +/* main function for the forked keygen slave */ +void keygen_child_main(OtrlUserState us, int infd, int outfd); +  /* mainloop handler for when a keygen finishes */  gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond); +/* copy the contents of file a to file b, overwriting it if it exists */ +void copyfile(const char *a, const char *b); + +/* read one line of input from a stream, excluding trailing newline */ +void myfgets(char *s, int size, FILE *stream); +  /* some yes/no handlers */  void yes_keygen(gpointer w, void *data);  void yes_forget_fingerprint(gpointer w, void *data); @@ -1416,43 +1426,154 @@ void show_otr_context_info(irc_t *irc, ConnContext *ctx)  void otr_keygen(irc_t *irc, const char *handle, const char *protocol)  { -	  	irc_usermsg(irc, "generating new private key for %s/%s...", handle, protocol); -	irc_usermsg(irc, "n/a: not implemented"); -	return;  	/* see if we already have a keygen child running. if not, start one and put a -	   handler on its output. -	    -	b_input_add(fd, GAIM_INPUT_READ, keygen_finish_handler, NULL); -	 -	   generate a fresh temp file name for our new key and save our current keys to it. -	   send the child filename and accountname/protocol for the new key -	   increment 'ntodo' */ +	   handler on its output. */ +	if(!irc->otr_keygen || waitpid(irc->otr_keygen, NULL, WNOHANG)) { +		pid_t p; +		int to[2], from[2]; +		FILE *tof, *fromf; +		 +		if(pipe(to) < 0 || pipe(from) < 0) { +			irc_usermsg(irc, "otr keygen: couldn't create pipe: %s", strerror(errno)); +			return; +		} +		 +		tof = fdopen(to[1], "w"); +		fromf = fdopen(from[0], "r"); +		if(!tof || !fromf) { +			irc_usermsg(irc, "otr keygen: couldn't streamify pipe: %s", strerror(errno)); +			return; +		} +		 +		p = fork(); +		if(p<0) { +			irc_usermsg(irc, "otr keygen: couldn't fork: %s", strerror(errno)); +			return; +		} +		 +		if(!p) { +			/* child process */ +			signal(SIGTERM, exit); +			keygen_child_main(irc->otr_us, to[0], from[1]); +			exit(0); +		} +		 +		irc->otr_keygen = p; +		irc->otr_to = tof; +		irc->otr_from = fromf; +		irc->otr_ntodo = 0; +		b_input_add(from[0], GAIM_INPUT_READ, keygen_finish_handler, irc); +	} -	/* in the child: -	   read filename, accountname, protocol from input, and start work: +	/* send accountname and protocol for the new key to our keygen slave */ +	fprintf(irc->otr_to, "%s\n%s\n", handle, protocol); +	fflush(irc->otr_to); +	irc->otr_ntodo++; +} -	result = otrl_privkey_generate(kg->irc->otr_us, kg->keyfile, kg->handle, -		kg->protocol); +void keygen_child_main(OtrlUserState us, int infd, int outfd) +{ +	FILE *input, *output; +	char filename[128], accountname[512], protocol[512]; +	gcry_error_t e; +	int tempfd; +	 +	input = fdopen(infd, "r"); +	output = fdopen(outfd, "w"); +	 +	while(!feof(input) && !ferror(input) && !feof(output) && !ferror(output)) { +		myfgets(accountname, 512, input); +		myfgets(protocol, 512, input); -		when done, send filename, accountname, and protocol to output. */ +		strncpy(filename, "/tmp/bitlbee-XXXXXX", 128); +		tempfd = mkstemp(filename); +		close(tempfd); + +		e = otrl_privkey_generate(us, filename, accountname, protocol); +		if(e) { +			fprintf(output, "\n");  /* this means failure */ +			fprintf(output, "otr keygen: %s\n", gcry_strerror(e)); +			unlink(filename); +		} else { +			fprintf(output, "%s\n", filename); +			fprintf(output, "otr keygen for %s/%s complete\n", accountname, protocol); +		} +		fflush(output); +	} +	fclose(input); +	fclose(output);  }  gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond)  { -	/* in the handler: -	   read filename, accountname, and protocol from child output -	   print a message to the user -			irc_usermsg(kg->irc, "otr keygen for %s/%s complete", kg->handle, kg->protocol); -	   load the file into userstate -	   call otr_save. -	   remove the tempfile. -	   decrement 'ntodo' -	   if 'ntodo' reaches zero, send SIGTERM to the child, waitpid for it, return FALSE */ - -	return TRUE;  /* still working, keep watching */ +	irc_t *irc = (irc_t *)data; +	char filename[512], msg[512]; + +	log_message(LOGLVL_DEBUG, "keygen_finish_handler cond=%d", cond); + +	myfgets(filename, 512, irc->otr_from); +	myfgets(msg, 512, irc->otr_from); +	 +	log_message(LOGLVL_DEBUG, "filename='%s'", filename); +	log_message(LOGLVL_DEBUG, "msg='%s'", msg); +	 +	irc_usermsg(irc, "%s", msg); +	if(filename[0]) { +		char *kf = g_strdup_printf("%s%s.otr_keys", global.conf->configdir, irc->nick); +		char *tmp = g_strdup_printf("%s.new", kf); +		copyfile(filename, tmp); +		unlink(filename); +		rename(tmp,kf); +		otrl_privkey_read(irc->otr_us, kf); +		g_free(kf); +		g_free(tmp); +	} +		 +	irc->otr_ntodo--; +	if(irc->otr_ntodo < 1) { +		/* okay, we think the slave is idle now, so kill him */ +		log_message(LOGLVL_DEBUG, "all keys done. die, slave!"); +		fclose(irc->otr_from); +		fclose(irc->otr_to); +		kill(irc->otr_keygen, SIGTERM); +		waitpid(irc->otr_keygen, NULL, 0); +		irc->otr_keygen = 0; +		return FALSE;  /* unregister ourselves */ +	} else { +		return TRUE;   /* slave still working, keep watching */ +	} +} + +void copyfile(const char *a, const char *b) +{ +	int fda, fdb; +	int n; +	char buf[1024]; +	 +	log_message(LOGLVL_DEBUG, "copyfile '%s' '%s'", a, b); +	 +	fda = open(a, O_RDONLY); +	fdb = open(b, O_WRONLY | O_CREAT | O_TRUNC, 0600); +	 +	while((n=read(fda, buf, 1024)) > 0) +		write(fdb, buf, n); +	 +	close(fda); +	close(fdb);	 +} + +void myfgets(char *s, int size, FILE *stream) +{ +	if(!fgets(s, size, stream)) { +		s[0] = '\0'; +	} else { +		int n = strlen(s); +		if(n>0 && s[n-1] == '\n') +			s[n-1] = '\0'; +	}  }  void yes_keygen(gpointer w, void *data) | 
