1 /* vi: set sw=4 ts=4: */
2 /*
3  * helper routines
4  *
5  * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
6  *
7  * Licensed under GPLv2, see file LICENSE in this source tree.
8  */
9 #if defined(__linux__)
10 # include <sys/prctl.h>
11 # define PRCTL
12 #elif defined(__FreeBSD__)
13 # include <sys/procctl.h>
14 # define PROCCTL
15 #endif
16 #include "libbb.h"
17 #include "mail.h"
18 
19 // common signal handler
signal_handler(int signo)20 static void signal_handler(int signo)
21 {
22 	if (SIGALRM == signo) {
23 		bb_simple_error_msg_and_die("timed out");
24 	}
25 
26 	// SIGCHLD. reap the zombie if we expect one
27 	if (G.helper_pid == 0)
28 		return;
29 #define status signo
30 	if (safe_waitpid(G.helper_pid, &status, WNOHANG) > 0) {
31 		G.helper_pid = 0;
32 		if (WIFSIGNALED(status))
33 			bb_error_msg_and_die("helper killed by signal %u", WTERMSIG(status));
34 		if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
35 			bb_error_msg_and_die("helper exited (%u)", WEXITSTATUS(status));
36 	}
37 #undef status
38 }
39 
launch_helper(const char ** argv)40 void FAST_FUNC launch_helper(const char **argv)
41 {
42 	pid_t pid;
43 	struct fd_pair child_out;
44 	struct fd_pair child_in;
45 
46 	xpiped_pair(child_out);
47 	xpiped_pair(child_in);
48 
49 	// NB: handler must be installed before vfork
50 	bb_signals(0
51 		+ (1 << SIGCHLD)
52 		+ (1 << SIGALRM)
53 		, signal_handler);
54 
55 	fflush_all();
56 	pid = xvfork();
57 	if (pid == 0) {
58 		// child
59 		close(child_in.wr);
60 		close(child_out.rd);
61 		xmove_fd(child_in.rd, STDIN_FILENO);
62 		xmove_fd(child_out.wr, STDOUT_FILENO);
63 		// if parent dies, get SIGTERM
64 #if defined(PRCTL)
65 		prctl(PR_SET_PDEATHSIG, SIGTERM, 0, 0, 0);
66 #elif defined(PROCCTL)
67 		{
68 			int signum = SIGTERM;
69 			procctl(P_PID, 0, PROC_PDEATHSIG_CTL, &signum);
70 		}
71 #endif
72 		// try to execute connection helper
73 		// NB: SIGCHLD & SIGALRM revert to SIG_DFL on exec
74 		BB_EXECVP_or_die((char**)argv);
75 	}
76 	G.helper_pid = pid;
77 	close(child_out.wr);
78 	close(child_in.rd);
79 	xmove_fd(child_out.rd, STDIN_FILENO);
80 	xmove_fd(child_in.wr, STDOUT_FILENO);
81 
82 	// parent goes on
83 }
84 
send_r_n(const char * s)85 void FAST_FUNC send_r_n(const char *s)
86 {
87 	if (G.verbose)
88 		bb_error_msg("send:'%s'", s);
89 	printf("%s\r\n", s);
90 }
91 
send_mail_command(const char * fmt,const char * param)92 char* FAST_FUNC send_mail_command(const char *fmt, const char *param)
93 {
94 	char *msg;
95 	if (G.timeout)
96 		alarm(G.timeout);
97 	msg = (char*)fmt;
98 	if (fmt) {
99 		msg = xasprintf(fmt, param);
100 		send_r_n(msg);
101 	}
102 	fflush_all();
103 	return msg;
104 }
105 
106 // NB: parse_url can modify url[] (despite const), but only if '@' is there
107 /*
108 static char* FAST_FUNC parse_url(char *url, char **user, char **pass)
109 {
110 	// parse [user[:pass]@]host
111 	// return host
112 	char *s = strchr(url, '@');
113 	*user = *pass = NULL;
114 	if (s) {
115 		*s++ = '\0';
116 		*user = url;
117 		url = s;
118 		s = strchr(*user, ':');
119 		if (s) {
120 			*s++ = '\0';
121 			*pass = s;
122 		}
123 	}
124 	return url;
125 }
126 */
127 
encode_n_base64(const char * fname,const char * text,size_t len)128 static void encode_n_base64(const char *fname, const char *text, size_t len)
129 {
130 	enum {
131 		SRC_BUF_SIZE = 57,  /* This *MUST* be a multiple of 3 */
132 		DST_BUF_SIZE = 4 * ((SRC_BUF_SIZE + 2) / 3),
133 	};
134 #define src_buf text
135 	char src[SRC_BUF_SIZE];
136 	FILE *fp = fp;
137 	char dst_buf[DST_BUF_SIZE + 1];
138 
139 	if (fname) {
140 		fp = (NOT_LONE_DASH(fname)) ? xfopen_for_read(fname) : stdin;
141 		src_buf = src;
142 	}
143 
144 	while (1) {
145 		size_t size;
146 		if (fname) {
147 			size = fread((char *)src_buf, 1, SRC_BUF_SIZE, fp);
148 			if ((ssize_t)size < 0)
149 				bb_simple_perror_msg_and_die(bb_msg_read_error);
150 		} else {
151 			size = len;
152 			if (len > SRC_BUF_SIZE)
153 				size = SRC_BUF_SIZE;
154 		}
155 		if (!size)
156 			break;
157 		// encode the buffer we just read in
158 		bb_uuencode(dst_buf, src_buf, size, bb_uuenc_tbl_base64);
159 		if (fname) {
160 			puts("");
161 		} else {
162 			src_buf += size;
163 			len -= size;
164 		}
165 		fwrite(dst_buf, 1, 4 * ((size + 2) / 3), stdout);
166 	}
167 	if (fname && NOT_LONE_DASH(fname))
168 		fclose(fp);
169 #undef src_buf
170 }
171 
printstr_base64(const char * text)172 void FAST_FUNC printstr_base64(const char *text)
173 {
174 	encode_n_base64(NULL, text, strlen(text));
175 }
176 
printbuf_base64(const char * text,unsigned len)177 void FAST_FUNC printbuf_base64(const char *text, unsigned len)
178 {
179 	encode_n_base64(NULL, text, len);
180 }
181 
printfile_base64(const char * fname)182 void FAST_FUNC printfile_base64(const char *fname)
183 {
184 	encode_n_base64(fname, NULL, 0);
185 }
186 
187 /*
188  * get username and password from a file descriptor
189  */
get_cred_or_die(int fd)190 void FAST_FUNC get_cred_or_die(int fd)
191 {
192 	if (isatty(fd)) {
193 		G.user = bb_ask_noecho(fd, /* timeout: */ 0, "User: ");
194 		G.pass = bb_ask_noecho(fd, /* timeout: */ 0, "Password: ");
195 	} else {
196 		G.user = xmalloc_reads(fd, /* maxsize: */ NULL);
197 		G.pass = xmalloc_reads(fd, /* maxsize: */ NULL);
198 	}
199 	if (!G.user || !*G.user || !G.pass)
200 		bb_simple_error_msg_and_die("no username or password");
201 }
202