/ tools / dserverdbg.c
dserverdbg.c
  1  #define _GNU_SOURCE
  2  #include <darlingserver/rpc.h>
  3  #include <unistd.h>
  4  #include <stdlib.h>
  5  #include <stdio.h>
  6  #include <string.h>
  7  #include <pwd.h>
  8  #include <fcntl.h>
  9  #include <sched.h>
 10  #include <errno.h>
 11  #include <signal.h>
 12  #include <sys/un.h>
 13  #include <sys/socket.h>
 14  
 15  typedef uint32_t mach_port_type_t;
 16  typedef uint32_t mach_port_right_t;
 17  
 18  #define MACH_PORT_RIGHT_SEND            ((mach_port_right_t) 0)
 19  #define MACH_PORT_RIGHT_RECEIVE         ((mach_port_right_t) 1)
 20  #define MACH_PORT_RIGHT_SEND_ONCE       ((mach_port_right_t) 2)
 21  #define MACH_PORT_RIGHT_PORT_SET        ((mach_port_right_t) 3)
 22  #define MACH_PORT_RIGHT_DEAD_NAME       ((mach_port_right_t) 4)
 23  #define MACH_PORT_RIGHT_LABELH          ((mach_port_right_t) 5) /* obsolete right */
 24  #define MACH_PORT_RIGHT_NUMBER          ((mach_port_right_t) 6) /* right not implemented */
 25  
 26  #define MACH_PORT_TYPE(right)                                           \
 27  	        ((mach_port_type_t)(((mach_port_type_t) 1)              \
 28  	        << ((right) + ((mach_port_right_t) 16))))
 29  #define MACH_PORT_TYPE_NONE         ((mach_port_type_t) 0L)
 30  #define MACH_PORT_TYPE_SEND         MACH_PORT_TYPE(MACH_PORT_RIGHT_SEND)
 31  #define MACH_PORT_TYPE_RECEIVE      MACH_PORT_TYPE(MACH_PORT_RIGHT_RECEIVE)
 32  #define MACH_PORT_TYPE_SEND_ONCE    MACH_PORT_TYPE(MACH_PORT_RIGHT_SEND_ONCE)
 33  #define MACH_PORT_TYPE_PORT_SET     MACH_PORT_TYPE(MACH_PORT_RIGHT_PORT_SET)
 34  #define MACH_PORT_TYPE_DEAD_NAME    MACH_PORT_TYPE(MACH_PORT_RIGHT_DEAD_NAME)
 35  #define MACH_PORT_TYPE_LABELH       MACH_PORT_TYPE(MACH_PORT_RIGHT_LABELH) /* obsolete */
 36  
 37  // borrowed from `src/startup/darling.c`
 38  // ---
 39  // Between Linux 4.9 and 4.11, a strange bug has been introduced
 40  // which prevents connecting to Unix sockets if the socket was
 41  // created in a different mount namespace or under overlayfs
 42  // (dunno which one is really responsible for this).
 43  #define USE_LINUX_4_11_HACK 1
 44  
 45  typedef enum dserverdbg_command {
 46  	dserverdbg_command_ps,
 47  	dserverdbg_command_lsport,
 48  	dserverdbg_command_lspset,
 49  	dserverdbg_command_lsmsg,
 50  } dserverdbg_command_t;
 51  
 52  struct sockaddr_un __dserver_socket_address_data = {0};
 53  int __dserver_main_thread_socket_fd = -1;
 54  
 55  static char* default_prefix_path(uid_t original_uid) {
 56  	struct passwd* info = getpwuid(original_uid);
 57  	char* result = NULL;
 58  
 59  	if (asprintf(&result, "%s/.darling", info->pw_dir) < 0) {
 60  		return NULL;
 61  	}
 62  
 63  	return result;
 64  };
 65  
 66  static char* get_prefix_path(uid_t original_uid) {
 67  	char* env = getenv("DPREFIX");
 68  
 69  	if (env) {
 70  		return strdup(env);
 71  	}
 72  
 73  	return default_prefix_path(original_uid);
 74  };
 75  
 76  // borrowed from `src/startup/darling.c`
 77  static void joinNamespace(pid_t pid, int type, const char* typeName)
 78  {
 79  	int fdNS;
 80  	char pathNS[4096];
 81  	
 82  	snprintf(pathNS, sizeof(pathNS), "/proc/%d/ns/%s", pid, typeName);
 83  
 84  	fdNS = open(pathNS, O_RDONLY);
 85  
 86  	if (fdNS < 0)
 87  	{
 88  		fprintf(stderr, "Cannot open %s namespace file: %s\n", typeName, strerror(errno));
 89  		exit(1);
 90  	}
 91  
 92  	// Calling setns() with a PID namespace doesn't move our process into it,
 93  	// but our child process will be spawned inside the namespace
 94  	if (setns(fdNS, type) != 0)
 95  	{
 96  		fprintf(stderr, "Cannot join %s namespace: %s\n", typeName, strerror(errno));
 97  		exit(1);
 98  	}
 99  	close(fdNS);
100  }
101  
102  // borrowed from `src/startup/darling.c`, with the UID/GID check removed
103  static pid_t getInitProcess(const char* prefix)
104  {
105  	const char pidFile[] = "/.init.pid";
106  	char* pidPath;
107  	pid_t pid;
108  	int pid_i;
109  	FILE *fp;
110  	char procBuf[100];
111  	char *exeBuf;
112  
113  	pidPath = (char*) alloca(strlen(prefix) + sizeof(pidFile));
114  	strcpy(pidPath, prefix);
115  	strcat(pidPath, pidFile);
116  
117  	fp = fopen(pidPath, "r");
118  	if (fp == NULL)
119  		return 0;
120  
121  	if (fscanf(fp, "%d", &pid_i) != 1)
122  	{
123  		fclose(fp);
124  		unlink(pidPath);
125  		return 0;
126  	}
127  	fclose(fp);
128  	pid = (pid_t) pid_i;
129  
130  	// Does the process exist?
131  	if (kill(pid, 0) == -1)
132  	{
133  		unlink(pidPath);
134  		return 0;
135  	}
136  
137  	// Is it actually an init process?
138  	snprintf(procBuf, sizeof(procBuf), "/proc/%d/comm", pid);
139  	fp = fopen(procBuf, "r");
140  	if (fp == NULL)
141  	{
142  		unlink(pidPath);
143  		return 0;
144  	}
145  
146  	if (fscanf(fp, "%ms", &exeBuf) != 1)
147  	{
148  		fclose(fp);
149  		unlink(pidPath);
150  		return 0;
151  	}
152  	fclose(fp);
153  
154  	if (strcmp(exeBuf, "darlingserver") != 0)
155  	{
156  		unlink(pidPath);
157  		return 0;
158  	}
159  	free(exeBuf);
160  
161  	return pid;
162  }
163  
164  static int setup_socket(void) {
165  	int fd = -1;
166  
167  	fd = socket(AF_UNIX, SOCK_DGRAM, 0);
168  	if (fd < 0) {
169  		goto err_out;
170  	}
171  
172  	int fd_flags = fcntl(fd, F_GETFD);
173  	if (fd_flags < 0) {
174  		goto err_out;
175  	}
176  	if (fcntl(fd, F_SETFD, fd_flags | FD_CLOEXEC) < 0) {
177  		goto err_out;
178  	}
179  
180  	sa_family_t family = AF_UNIX;
181  	if (bind(fd, (const struct sockaddr*)&family, sizeof(family)) < 0) {
182  		goto err_out;
183  	}
184  
185  out:
186  	return fd;
187  
188  err_out:
189  	if (fd >= 0) {
190  		close(fd);
191  	}
192  
193  	return -1;
194  };
195  
196  // borrowed from `src/startup/darling.c`
197  static void missingSetuidRoot(void)
198  {
199  	char path[4096];
200  	int len;
201  
202  	len = readlink("/proc/self/exe", path, sizeof(path)-1);
203  	if (len < 0)
204  		strcpy(path, "darling");
205  	else
206  		path[len] = '\0';
207  
208  	fprintf(stderr, "Sorry, the `%s' binary is not setuid root, which is mandatory.\n", path);
209  	fprintf(stderr, "Darling needs this in order to create mount and PID namespaces and to perform mounts.\n");
210  }
211  
212  int main(int argc, char** argv) {
213  	char* prefix_path = NULL;
214  	dserverdbg_command_t command = dserverdbg_command_ps;
215  	pid_t command_pid = 0;
216  	uint32_t command_port = 0;
217  	int output_fd = -1;
218  	int status = 0;
219  	uint64_t count = 0;
220  	uint64_t elmsize = 0;
221  	char* data = 0;
222  	uid_t original_uid = -1;
223  	gid_t original_gid = -1;
224  #if USE_LINUX_4_11_HACK
225  	pid_t pidInit = 0;
226  #endif
227  
228  	if (geteuid() != 0) {
229  		missingSetuidRoot();
230  		return 1;
231  	}
232  
233  	original_uid = getuid();
234  	original_gid = getgid();
235  
236  	setuid(0);
237  	setgid(0);
238  
239  	prefix_path = get_prefix_path(original_uid);
240  	if (!prefix_path) {
241  		fprintf(stderr, "Failed to determine prefix path\n");
242  		return 1;
243  	}
244  
245  	__dserver_socket_address_data.sun_family = AF_UNIX;
246  	snprintf(__dserver_socket_address_data.sun_path, sizeof(__dserver_socket_address_data.sun_path), "%s/.darlingserver.sock", prefix_path);
247  
248  #if USE_LINUX_4_11_HACK
249  	pidInit = getInitProcess(prefix_path);
250  	joinNamespace(pidInit, CLONE_NEWNS, "mnt");
251  #endif
252  
253  	__dserver_main_thread_socket_fd = setup_socket();
254  
255  	if (__dserver_main_thread_socket_fd < 0) {
256  		fprintf(stderr, "Failed to set up darlingserver client socket\n");
257  		return 1;
258  	}
259  
260  	if (argc > 1) {
261  		if (strcmp(argv[1], "ps") == 0 || strcmp(argv[1], "lsproc") == 0) {
262  			command = dserverdbg_command_ps;
263  		} else if (strcmp(argv[1], "lsport") == 0) {
264  			command = dserverdbg_command_lsport;
265  		} else if (strcmp(argv[1], "lspset") == 0) {
266  			command = dserverdbg_command_lspset;
267  		} else if (strcmp(argv[1], "lsmsg") == 0) {
268  			command = dserverdbg_command_lsmsg;
269  		} else {
270  			fprintf(stderr, "Unknown subcommand: %s\n", argv[1]);
271  			return 1;
272  		}
273  	}
274  
275  	switch (command) {
276  		case dserverdbg_command_ps:
277  			if (argc > 2) {
278  				fprintf(stderr, "Expected 1 argument (subcommand); got %d arguments\n", argc);
279  				return 1;
280  			}
281  			break;
282  		case dserverdbg_command_lsport:
283  			if (argc != 3) {
284  				fprintf(stderr, "Expected 2 arguments (subcommand and PID); got %d arguments\n", argc);
285  				return 1;
286  			}
287  			command_pid = atoi(argv[2]);
288  			break;
289  		case dserverdbg_command_lspset:
290  		case dserverdbg_command_lsmsg:
291  			if (argc != 4) {
292  				fprintf(stderr, "Expected 3 arguments (subcommand, PID, and port name); got %d arguments\n", argc);
293  				return 1;
294  			}
295  			command_pid = atoi(argv[2]);
296  			command_port = atoi(argv[3]);
297  			break;
298  	}
299  
300  	switch (command) {
301  		case dserverdbg_command_ps:
302  			status = dserver_rpc_debug_list_processes(&count, &output_fd);
303  			elmsize = sizeof(dserver_debug_process_t);
304  			break;
305  		case dserverdbg_command_lsport:
306  			status = dserver_rpc_debug_list_ports(command_pid, &count, &output_fd);
307  			elmsize = sizeof(dserver_debug_port_t);
308  			break;
309  		case dserverdbg_command_lspset:
310  			status = dserver_rpc_debug_list_members(command_pid, command_port, &count, &output_fd);
311  			elmsize = sizeof(dserver_debug_port_t);
312  			break;
313  		case dserverdbg_command_lsmsg:
314  			status = dserver_rpc_debug_list_messages(command_pid, command_port, &count, &output_fd);
315  			elmsize = sizeof(dserver_debug_message_t);
316  			break;
317  	}
318  
319  	if (status != 0) {
320  		fprintf(stderr, "Subcommand failed: server replied with error: %d (%s)\n", status, strerror(status));
321  		return 1;
322  	}
323  
324  	for (uint64_t i = 0; i < count; ++i) {
325  		char buffer[elmsize];
326  
327  		if (read(output_fd, buffer, elmsize) != elmsize) {
328  			status = errno;
329  			fprintf(stderr, "Failed to read from output pipe: %d (%s)\n", status, strerror(status));
330  			return 1;
331  		}
332  
333  		switch (command) {
334  			case dserverdbg_command_ps: {
335  				dserver_debug_process_t* data = (void*)buffer;
336  				printf("pid %u - %lu ports\n", data->pid, data->port_count);
337  			} break;
338  
339  			case dserverdbg_command_lsport:
340  			case dserverdbg_command_lspset: {
341  				dserver_debug_port_t* data = (void*)buffer;
342  				const char* right_name = "<unknown>";
343  
344  				if (data->rights == MACH_PORT_TYPE_SEND) {
345  					right_name = "send";
346  				} else if (data->rights == MACH_PORT_TYPE_RECEIVE) {
347  					right_name = "receive";
348  				} else if (data->rights == MACH_PORT_TYPE_SEND_ONCE) {
349  					right_name = "send-once";
350  				} else if (data->rights == MACH_PORT_TYPE_PORT_SET) {
351  					right_name = "port set";
352  				} else if (data->rights == MACH_PORT_TYPE_DEAD_NAME) {
353  					right_name = "dead name";
354  				} else if (data->rights == MACH_PORT_TYPE_LABELH) {
355  					right_name = "labelh";
356  				}
357  
358  				printf("port %d (%s), %lu refs - %lu messages\n", data->port_name, right_name, data->refs, data->messages);
359  			} break;
360  
361  			case dserverdbg_command_lsmsg: {
362  				dserver_debug_message_t* data = (void*)buffer;
363  
364  				printf("message %lu (from %u); %lu bytes\n", i, data->sender, data->size);
365  			} break;
366  		}
367  	}
368  
369  	if (output_fd >= 0) {
370  		close(output_fd);
371  	}
372  
373  	if (prefix_path) {
374  		free(prefix_path);
375  	}
376  
377  	return 0;
378  };