sockroot.c (3648B) download
1#include <errno.h>
2#include <signal.h>
3#include <stdio.h>
4#include <stdlib.h>
5#include <string.h>
6#include <sys/socket.h>
7#include <sys/stat.h>
8#include <sys/un.h>
9#include <sys/wait.h>
10#include <unistd.h>
11
12#define SERVER_SOCK_FILE "sockroot.sock"
13#define CHROOT "test/"
14#define EXECUTE "/bin/sh"
15#define SOCKET_LIMIT 64
16
17int processes[SOCKET_LIMIT];
18int processes_size = 0;
19
20void handle_client(int client) {
21 int pid;
22
23 if (processes_size == SOCKET_LIMIT) {
24 fprintf(stderr, "error: cannot accept client: process-limit reached\n");
25 return;
26 }
27
28 while ((pid = fork()) == -1) {
29 fprintf(stderr, "warning: fork for process: %s, waiting 1sec...\n", strerror(errno));
30 sleep(1);
31 }
32
33 if (pid == 0) { // is child
34 dup2(client, 0);
35 dup2(client, 1);
36 dup2(client, 2);
37 close(client);
38
39 if (chroot(CHROOT) == -1) {
40 fprintf(stderr, "error: cannot chroot to '" CHROOT "': %s\n", strerror(errno));
41 _exit(1);
42 }
43 if (chdir("/") == -1) {
44 fprintf(stderr, "error: cannot chdir to '/': %s\n", strerror(errno));
45 _exit(1);
46 }
47
48 execl(EXECUTE, EXECUTE, "-i", NULL);
49 fprintf(stderr, "error: cannot execute '" EXECUTE "': %s\n", strerror(errno));
50 _exit(1);
51 }
52
53 close(client);
54
55 printf("new connection: %d\n", pid);
56
57 for (int i = 0; i < SOCKET_LIMIT; i++) {
58 if (processes[i] == 0) {
59 processes[i] = pid;
60 processes_size++;
61 return;
62 }
63 }
64}
65
66void on_child(int signal) {
67 (void) signal;
68
69 int pid, status;
70 if ((pid = wait(&status)) == -1) {
71 fprintf(stderr, "error: cannot await child: %s\n", strerror(errno));
72 return;
73 }
74 printf("%d terminated\n", pid);
75
76 for (int i = 0; i < SOCKET_LIMIT; i++) {
77 if (processes[i] == pid) {
78 processes[i] = 0;
79 processes_size--;
80 return;
81 }
82 }
83 fprintf(stderr, "error: cannot find %d in process-table\n", pid);
84}
85
86void on_interrupt(int signal) {
87 (void) signal;
88
89 for (int i = 0; i < SOCKET_LIMIT; i++) {
90 if (processes[i] > 0 && kill(processes[i], SIGTERM) == -1) {
91 fprintf(stderr, "warning: unable to terminate %d: %s\n", processes[i], strerror(errno));
92 }
93 }
94
95 while (processes_size > 0) {
96 printf("awaiting %d processes to stop\n", processes_size);
97 sleep(1);
98 }
99
100 if (processes_size > 0) {
101 printf("kill %d left over processes\n", processes_size);
102 for (int i = 0; i < SOCKET_LIMIT; i++) {
103 if (processes[i] > 0 && kill(processes[i], SIGKILL) == -1) {
104 fprintf(stderr, "warning: unable to kill %d: %s\n", processes[i], strerror(errno));
105 }
106 }
107 sleep(1);
108 }
109 exit(0);
110}
111
112int main(void) {
113 if (unlink(SERVER_SOCK_FILE) == -1 && errno != ENOENT) {
114 fprintf(stderr, "error: cannot unlink socket: %s\n", strerror(errno));
115 return 1;
116 }
117
118 int server;
119
120 if ((server = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
121 fprintf(stderr, "error: cannot create socket: %s\n", strerror(errno));
122 return 1;
123 }
124
125 umask(0);
126
127 // bind socket to address
128 struct sockaddr_un addr = { 0 };
129 addr.sun_family = AF_UNIX;
130 strncpy(addr.sun_path, SERVER_SOCK_FILE, sizeof(addr.sun_path));
131 if (bind(server, (struct sockaddr*) &addr, sizeof(addr)) == -1) {
132 fprintf(stderr, "error: cannot bind to socket: %s\n", strerror(errno));
133 return 1;
134 }
135
136 // listen for connections
137 if (listen(server, 5) == -1) {
138 fprintf(stderr, "error: cannot listen socket: %s\n", strerror(errno));
139 return 1;
140 }
141
142 for (int i = 0; i < SOCKET_LIMIT; i++) {
143 processes[i] = 0;
144 }
145
146 signal(SIGINT, on_interrupt);
147 signal(SIGCHLD, on_child);
148
149 while (1) {
150 int client_fd;
151 if ((client_fd = accept(server, NULL, NULL)) == -1) {
152 fprintf(stderr, "error: cannot accept client from socket: %s\n", strerror(errno));
153 continue;
154 }
155 handle_client(client_fd);
156 }
157
158 return 0;
159}