1 /*
2  * Copyright 2014-2016, Björn Ståhl
3  * License: 3-Clause BSD, see COPYING file in arcan source repository.
4  * Reference: http://arcan-fe.com
5  */
6 
7 #include <stdio.h>
8 #include <stdint.h>
9 #include <stdbool.h>
10 #include <string.h>
11 #include <stdlib.h>
12 #include <unistd.h>
13 #include <errno.h>
14 #include <assert.h>
15 #include <dirent.h>
16 
17 #include <sys/types.h>
18 #include <sys/mman.h>
19 #include <sys/stat.h>
20 #include <sys/socket.h>
21 #include <sys/uio.h>
22 #include <sys/time.h>
23 #include <signal.h>
24 #include <fcntl.h>
25 #include <time.h>
26 #include <dlfcn.h>
27 
28 #include <arcan_shmif.h>
29 #include "frameserver.h"
30 
31 #ifndef AFSRV_CHAINLOADER
close_logdev()32 static void close_logdev()
33 {
34 	fflush(stderr);
35 }
36 
toggle_logdev(const char * prefix)37 static void toggle_logdev(const char* prefix)
38 {
39 	const char* const logdir = getenv("ARCAN_FRAMESERVER_LOGDIR");
40 
41 	if (!prefix || !logdir)
42 		return;
43 
44 	char timeb[16];
45 	time_t t = time(NULL);
46 	struct tm* basetime = localtime(&t);
47 	strftime(timeb, sizeof(timeb)-1, "%y%m%d_%H%M", basetime);
48 
49 	size_t logbuf_sz = strlen(logdir) +
50 		sizeof("/fsrv__yymmddhhss__65536.txt") + strlen(prefix);
51 	char* logbuf = malloc(logbuf_sz);
52 
53 	snprintf(logbuf, logbuf_sz,
54 		"%s/fsrv_%s_%s_%d.txt", logdir, prefix, timeb, (int)getpid());
55 	if (!freopen(logbuf, "a", stderr)){
56 		if (!freopen("/dev/null", "a", stderr))
57 			fclose(stderr);
58 	}
59 }
60 #endif
61 
dumpargs(int argc,char ** argv)62 static void dumpargs(int argc, char** argv)
63 {
64 	printf("invalid number of arguments (%d):\n", argc);
65  	printf("[1 mode] : %s\n", argc > 1 && argv[1] ? argv[1] : "");
66 	printf("environment (ARCAN_ARG) : %s\n",
67 		getenv("ARCAN_ARG") ? getenv("ARCAN_ARG") : "");
68 	printf("environment (ARCAN_SOCKIN_FD) : %s\n",
69 		getenv("ARCAN_SOCKIN_FD") ? getenv("ARCAN_SOCKIN_FD") : "");
70 	printf("environment (ARCAN_CONNPATH) : %s\n",
71 		getenv("ARCAN_CONNPATH") ? getenv("ARCAN_CONNPATH") : "");
72 	printf("environment (ARCAN_CONNKEY) : %s\n",
73 		getenv("ARCAN_CONKEY") ? getenv("ARCAN_CONNKEY") : "");
74 }
75 
76 #if defined(_DEBUG) && !defined(__APPLE__) && !defined(__BSD)
77 
dump_links(const char * path)78 void dump_links(const char* path)
79 {
80 	DIR* dp;
81 	struct dirent64* dirp;
82 
83 	if ((dp = opendir(path)) == NULL)
84 		return;
85 
86 	int fd = dirfd(dp);
87 
88 	while((dirp = readdir64(dp)) != NULL){
89 		if (strcmp(dirp->d_name, ".") == 0)
90 			continue;
91 		else if (strcmp(dirp->d_name, "..") == 0)
92 			continue;
93 
94 		char buf[256];
95 		buf[255] = '\0';
96 
97 		ssize_t nr = readlinkat(fd, dirp->d_name, buf, sizeof(buf)-1);
98 		if (-1 == nr)
99 			continue;
100 
101 		buf[nr] = '\0';
102 		fprintf(stdout, "\t%s\n", buf);
103 	}
104 
105 	closedir(dp);
106 }
107 #endif
108 
109 /*
110  * When built as a chainloader we select a different binary (our own
111  * name or afsrv if we're arcan_frameserver) + _mode and just pass
112  * the environment onwards. The afsrv_ split permits the parent to run
113  * with a different set of frameservers for debugging/testing/etc. purposes.
114  *
115  * The chainloading approach is to get a process separated spot for
116  * implementing monitoring, sandboxing and other environment related factors
117  * where we might have temporarily inflated privileges.
118  */
119 #ifdef AFSRV_CHAINLOADER
main(int argc,char ** argv)120 int main(int argc, char** argv)
121 {
122 	if (2 != argc){
123 		dumpargs(argc, argv);
124 		return EXIT_FAILURE;
125 	}
126 
127 	if (!argv[0])
128 		return EXIT_FAILURE;
129 
130 	size_t i = strlen(argv[0])-1;
131 	for (; i && argv[0][i] == '/'; i--) argv[0][i] = 0;
132 	for (; i && argv[0][i-1] != '/'; i--);
133 	if (i)
134 		argv[0][i-1] = '\0';
135 
136 	const char* dirn = argv[0];
137 	const char* base = argv[0] + i;
138 	const char* mode = argv[1];
139 
140 	if (strcmp(base, "arcan_frameserver") == 0)
141 		base = "afsrv";
142 
143 	size_t bin_sz = strlen(dirn) + strlen(base) + strlen(argv[1]) + 3;
144 	char newarg[ bin_sz ];
145 	snprintf(newarg, bin_sz, "%s/%s_%s", dirn, base, argv[1]);
146 
147 /*
148  * the sweet-spot for adding in privilege/uid/gid swapping and setting up mode-
149  * specific sandboxing, package format mount (or other compatibility / loading)
150  * etc. When that is done, ofc. add more stringent control over what newarg
151  * turns out to be.
152  */
153 
154 /* we no longer need the mode argument */
155 	argv[1] = NULL;
156 	argv[0] = newarg;
157 
158 	setsid();
159 
160 	execv(newarg, argv);
161 
162 	return EXIT_FAILURE;
163 }
164 
165 #else
166 typedef int (*mode_fun)(struct arcan_shmif_cont*, struct arg_arr*);
167 
launch_mode(const char * modestr,mode_fun fptr,enum ARCAN_SEGID id,enum ARCAN_FLAGS flags,char * altarg)168 int launch_mode(const char* modestr,
169 	mode_fun fptr, enum ARCAN_SEGID id, enum ARCAN_FLAGS flags, char* altarg)
170 {
171 	char* debug = getenv("ARCAN_FRAMESERVER_DEBUGSTALL");
172 
173 	if (!debug)
174 		toggle_logdev(modestr);
175 
176 	struct arg_arr* arg = NULL;
177 	struct arcan_shmif_cont con = arcan_shmif_open_ext(flags,
178 		&arg, (struct shmif_open_ext){.type = id}, sizeof(struct shmif_open_ext));
179 
180 	if (!arg && altarg)
181 		arg = arg_unpack(altarg);
182 
183 	if (debug){
184 		arcan_shmif_signal(&con, SHMIF_SIGVID);
185 		int sleeplen = strtoul(debug, NULL, 10);
186 
187 		struct arcan_event ev = {
188 			.ext.kind = ARCAN_EVENT(IDENT)
189 		};
190 		arcan_shmif_enqueue(&con, &ev);
191 		snprintf(
192 			(char*)ev.ext.message.data,
193 			sizeof(ev.ext.message.data), "debugstall:%d:%zu", sleeplen, (size_t)getpid()
194 		);
195 
196 		if (sleeplen <= 0){
197 			fprintf(stdout, "\x1b[1mARCAN_FRAMESERVER_DEBUGSTALL,\x1b[0m "
198 				"spin-waiting for debugger.\n \tAttach to pid: "
199 				"\x1b[32m%d\x1b[39m\x1b[0m and break out of loop"
200 				" (set loop = 0)\n", getpid()
201 			);
202 
203 			volatile int loop = 1;
204 			while(loop == 1);
205 		}
206 		else{
207 			fprintf(stdout,"\x1b[1mARCAN_FRAMESERVER_DEBUGSTALL set, waiting %d s.\n"
208 				"\tfor debugging/tracing, attach to pid: \x1b[32m%d\x1b[39m\x1b[0m\n",
209 				sleeplen, (int) getpid());
210 			sleep(sleeplen);
211 		}
212 	}
213 
214 	return fptr(con.addr ? &con : NULL, arg);
215 }
216 
main(int argc,char ** argv)217 int main(int argc, char** argv)
218 {
219 #ifdef DEFAULT_FSRV_MODE
220 	char* fsrvmode = DEFAULT_FSRV_MODE;
221 	char* argstr = argc > 1 ? argv[1] : NULL; /* optional */
222 #else
223 /* non-split mode arguments require [mode + opt-arg ] */
224 	char* fsrvmode = argv[1];
225 	char* argstr = argc > 2 ? argv[2] : NULL;
226 #endif
227 
228 /*
229  * Monitor for descriptor leaks from parent
230  */
231 #if defined(_DEBUG) && !defined(__APPLE__) && !defined(__BSD)
232 	DIR* dp;
233 	struct dirent64* dirp;
234 	if ((dp = opendir("/proc/self/fd")) != NULL){
235 		size_t desc_count = 0;
236 
237 		while((dirp = readdir64(dp)) != NULL) {
238 			if (strcmp(dirp->d_name, ".") != 0 && strcmp(dirp->d_name, "..") != 0)
239 				desc_count++;
240 		}
241 		closedir(dp);
242 
243 /* stdin, stdout, stderr, [connection socket], any more
244  * and we should be suspicious about descriptor leakage */
245 		if (desc_count > 5){
246 			fprintf(stdout, "\x1b[1msuspicious amount (%zu)"
247 				"of descriptors open, investigate.\x1b[0m\n", desc_count);
248 
249 			dump_links("/proc/self/fd");
250 		}
251 	}
252 #endif
253 
254 /*
255  * These are enabled based on build-system toggles,
256  * a global define, FRAMESERVER_MODESTRING includes a space
257  * separated list of enabled frameserver archetypes.
258  */
259 #ifdef ENABLE_FSRV_DECODE
260 	if (strcmp(fsrvmode, "decode") == 0)
261 		return launch_mode("decode",
262 			afsrv_decode, SEGID_MEDIA,
263 			SHMIF_MANUAL_PAUSE | SHMIF_NOACTIVATE_RESIZE, argstr
264 		);
265 #endif
266 
267 #ifdef ENABLE_FSRV_TERMINAL
268 	if (strcmp(fsrvmode, "terminal") == 0)
269 		return launch_mode("terminal", afsrv_terminal, SEGID_TERMINAL, 0, argstr);
270 #endif
271 
272 #ifdef ENABLE_FSRV_ENCODE
273 	if (strcmp(fsrvmode, "encode") == 0)
274 		return launch_mode("encode", afsrv_encode, SEGID_ENCODER, 0, argstr);
275 #endif
276 
277 #ifdef ENABLE_FSRV_REMOTING
278 	if (strcmp(fsrvmode, "remoting") == 0)
279 		return launch_mode("remoting", afsrv_remoting, SEGID_REMOTING, 0, argstr);
280 #endif
281 
282 #ifdef ENABLE_FSRV_GAME
283 	if (strcmp(fsrvmode, "game") == 0)
284 		return launch_mode("game", afsrv_game,
285 			SEGID_GAME, SHMIF_NOACTIVATE_RESIZE, argstr);
286 #endif
287 
288 #ifdef ENABLE_FSRV_AVFEED
289 	if (strcmp(fsrvmode, "avfeed") == 0)
290 		return launch_mode("avfeed", afsrv_avfeed,
291 			SEGID_MEDIA, SHMIF_DISABLE_GUARD, argstr);
292 #endif
293 
294 /*
295  * NET- is a bit special in that it encompasses multiple submodes
296  * and may need to support multiple more (p2p-node etc.)
297  * which may need different IDs, so we do a preliminary arg-unpack
298  * in beforehand.
299  */
300 #ifdef ENABLE_FSRV_NET
301 	if (strcmp(fsrvmode, "net") == 0){
302 		struct arg_arr* tmp = arg_unpack(getenv("ARCAN_ARG"));
303 		const char* rk;
304 
305 		if (!tmp)
306 			tmp = arg_unpack(argstr);
307 
308 		enum ARCAN_SEGID id;
309 		mode_fun fptr;
310 		const char* modestr = NULL;
311 
312 		if (tmp && arg_lookup(tmp, "mode", 0, &rk)){
313 			if (strcmp(rk, "client") == 0){
314 				id = SEGID_NETWORK_CLIENT;
315 				fptr = afsrv_netcl;
316 				modestr = "client";
317 			}
318 			else if (strcmp(rk, "server") == 0){
319 				id = SEGID_NETWORK_SERVER;
320 				fptr = afsrv_netsrv;
321 				modestr = "server";
322 			}
323 			else{
324 				fprintf(stdout, "frameserver_net, invalid ARCAN_ARG env:\n"
325 					"must have mode=modev set to client or server.\n");
326 				return EXIT_FAILURE;
327 			}
328 		}
329  /* will invalidate all aliases from _lookup */
330 		arg_cleanup(tmp);
331 
332 		if (!modestr){
333 			fprintf(stdout, "frameserver_net, invalid ARCAN_ARG env:\n"
334 				"must have mode=modev set to client or server.\n");
335 			return EXIT_FAILURE;
336 		}
337 
338 		return launch_mode(modestr, fptr, id, 0, argstr);
339 	}
340 #endif
341 
342 	printf("frameserver launch failed, unsupported mode (%s)\n", fsrvmode);
343 	dumpargs(argc, argv);
344 
345 	return EXIT_FAILURE;
346 }
347 #endif
348