xref: /aosp_15_r20/external/libcap/contrib/capso/capso.c (revision 2810ac1b38eead2603277920c78344c84ddf3aff)
1*2810ac1bSKiyoung Kim /*
2*2810ac1bSKiyoung Kim  * Worked example for a shared object with a file capability on it
3*2810ac1bSKiyoung Kim  * leveraging itself for preprogrammed functionality.
4*2810ac1bSKiyoung Kim  *
5*2810ac1bSKiyoung Kim  * This example implements a shared library that can bind to
6*2810ac1bSKiyoung Kim  * the privileged port. ":80".
7*2810ac1bSKiyoung Kim  *
8*2810ac1bSKiyoung Kim  * The shared library needs to be installed with
9*2810ac1bSKiyoung Kim  * cap_net_bind_service=p. As a shared library, it provides the
10*2810ac1bSKiyoung Kim  * function bind80().
11*2810ac1bSKiyoung Kim  */
12*2810ac1bSKiyoung Kim 
13*2810ac1bSKiyoung Kim #define _GNU_SOURCE
14*2810ac1bSKiyoung Kim 
15*2810ac1bSKiyoung Kim #include <dlfcn.h>
16*2810ac1bSKiyoung Kim #include <netdb.h>
17*2810ac1bSKiyoung Kim #include <stdio.h>
18*2810ac1bSKiyoung Kim #include <stdlib.h>
19*2810ac1bSKiyoung Kim #include <string.h>
20*2810ac1bSKiyoung Kim #include <sys/capability.h>
21*2810ac1bSKiyoung Kim #include <sys/socket.h>
22*2810ac1bSKiyoung Kim #include <sys/types.h>
23*2810ac1bSKiyoung Kim #include <sys/un.h>
24*2810ac1bSKiyoung Kim #include <sys/wait.h>
25*2810ac1bSKiyoung Kim #include <unistd.h>
26*2810ac1bSKiyoung Kim 
27*2810ac1bSKiyoung Kim #include "capso.h"
28*2810ac1bSKiyoung Kim 
29*2810ac1bSKiyoung Kim extern char **environ;
30*2810ac1bSKiyoung Kim 
31*2810ac1bSKiyoung Kim /*
32*2810ac1bSKiyoung Kim  * fake_exploit is some dedicated code to simulate a shell escape type
33*2810ac1bSKiyoung Kim  * exploit. This is obviously not something serious to include in code
34*2810ac1bSKiyoung Kim  * that has actually been audited for security, but we use it to
35*2810ac1bSKiyoung Kim  * demonstrate an aspect of file capabilities vs. setuid root for
36*2810ac1bSKiyoung Kim  * granting privilege.
37*2810ac1bSKiyoung Kim  */
fake_exploit(void)38*2810ac1bSKiyoung Kim static void fake_exploit(void) {
39*2810ac1bSKiyoung Kim #ifdef ALLOW_EXPLOIT
40*2810ac1bSKiyoung Kim     const char *exploit = getenv("TRIGGER_EXPLOIT");
41*2810ac1bSKiyoung Kim     if (exploit == NULL) {
42*2810ac1bSKiyoung Kim 	return;
43*2810ac1bSKiyoung Kim     }
44*2810ac1bSKiyoung Kim 
45*2810ac1bSKiyoung Kim     switch (*exploit) {
46*2810ac1bSKiyoung Kim     case '^':
47*2810ac1bSKiyoung Kim     case '%':
48*2810ac1bSKiyoung Kim 	exploit++;
49*2810ac1bSKiyoung Kim 	cap_value_t caps = CAP_NET_BIND_SERVICE;
50*2810ac1bSKiyoung Kim 	cap_t c = cap_get_proc();
51*2810ac1bSKiyoung Kim 	cap_set_flag(c, CAP_INHERITABLE, 1, &caps, CAP_SET);
52*2810ac1bSKiyoung Kim 	if (cap_set_proc(c)) {
53*2810ac1bSKiyoung Kim 	    perror("Failed to raise inheritable capability");
54*2810ac1bSKiyoung Kim 	    exit(1);
55*2810ac1bSKiyoung Kim 	}
56*2810ac1bSKiyoung Kim 	if (*(exploit-1) == '%') {
57*2810ac1bSKiyoung Kim 	    break;
58*2810ac1bSKiyoung Kim 	}
59*2810ac1bSKiyoung Kim 	cap_free(c);
60*2810ac1bSKiyoung Kim 	if (cap_set_ambient(caps, CAP_SET) != 0) {
61*2810ac1bSKiyoung Kim 	    perror("Unable to raise ambient capability");
62*2810ac1bSKiyoung Kim 	    exit(1);
63*2810ac1bSKiyoung Kim 	}
64*2810ac1bSKiyoung Kim 	break;
65*2810ac1bSKiyoung Kim     }
66*2810ac1bSKiyoung Kim 
67*2810ac1bSKiyoung Kim     char *ts = strdup(exploit);
68*2810ac1bSKiyoung Kim     if (ts == NULL) {
69*2810ac1bSKiyoung Kim 	perror("Failed to duplicate exploit string");
70*2810ac1bSKiyoung Kim 	exit(1);
71*2810ac1bSKiyoung Kim     }
72*2810ac1bSKiyoung Kim 
73*2810ac1bSKiyoung Kim     int i, j, n = 1;
74*2810ac1bSKiyoung Kim     for (i = 0; ts[i]; i++) {
75*2810ac1bSKiyoung Kim 	switch (ts[i]) {
76*2810ac1bSKiyoung Kim 	case ' ':
77*2810ac1bSKiyoung Kim 	case '\t':
78*2810ac1bSKiyoung Kim 	    n++;
79*2810ac1bSKiyoung Kim 	    ts[i] = '\0';
80*2810ac1bSKiyoung Kim 	}
81*2810ac1bSKiyoung Kim     }
82*2810ac1bSKiyoung Kim     char **argv = calloc(n, sizeof(char *));
83*2810ac1bSKiyoung Kim     for (i = 0, j = 0; j < n; j++) {
84*2810ac1bSKiyoung Kim 	char *s = ts+i;
85*2810ac1bSKiyoung Kim 	argv[j] = s;
86*2810ac1bSKiyoung Kim 	i += 1 + strlen(s);
87*2810ac1bSKiyoung Kim 	printf("execv argv[%d] = \"%s\"\n", j, s);
88*2810ac1bSKiyoung Kim     }
89*2810ac1bSKiyoung Kim 
90*2810ac1bSKiyoung Kim     execv(argv[0], argv);
91*2810ac1bSKiyoung Kim     perror("Execv failed");
92*2810ac1bSKiyoung Kim     exit(1);
93*2810ac1bSKiyoung Kim #endif /* def ALLOW_EXPLOIT */
94*2810ac1bSKiyoung Kim }
95*2810ac1bSKiyoung Kim 
96*2810ac1bSKiyoung Kim /*
97*2810ac1bSKiyoung Kim  * where_am_i determines the full path for the shared libary that
98*2810ac1bSKiyoung Kim  * contains this function. It allocates the path in strdup()d memory
99*2810ac1bSKiyoung Kim  * that should be free()d by the caller. If it can't find itself, it
100*2810ac1bSKiyoung Kim  * returns NULL.
101*2810ac1bSKiyoung Kim  */
where_am_i(void)102*2810ac1bSKiyoung Kim static char *where_am_i(void)
103*2810ac1bSKiyoung Kim {
104*2810ac1bSKiyoung Kim     Dl_info info;
105*2810ac1bSKiyoung Kim     if (dladdr(where_am_i, &info) == 0) {
106*2810ac1bSKiyoung Kim 	return NULL;
107*2810ac1bSKiyoung Kim     }
108*2810ac1bSKiyoung Kim     return strdup(info.dli_fname);
109*2810ac1bSKiyoung Kim }
110*2810ac1bSKiyoung Kim 
111*2810ac1bSKiyoung Kim /*
112*2810ac1bSKiyoung Kim  * try_bind80 attempts to reuseably bind to port 80 with the given
113*2810ac1bSKiyoung Kim  * hostname. It returns a bound filedescriptor or -1 on error.
114*2810ac1bSKiyoung Kim  */
try_bind80(const char * hostname)115*2810ac1bSKiyoung Kim static int try_bind80(const char *hostname)
116*2810ac1bSKiyoung Kim {
117*2810ac1bSKiyoung Kim     struct addrinfo *conf, *detail = NULL;
118*2810ac1bSKiyoung Kim     int err, ret = -1, one = 1;
119*2810ac1bSKiyoung Kim 
120*2810ac1bSKiyoung Kim     conf = calloc(1, sizeof(*conf));
121*2810ac1bSKiyoung Kim     if (conf == NULL) {
122*2810ac1bSKiyoung Kim       return -1;
123*2810ac1bSKiyoung Kim     }
124*2810ac1bSKiyoung Kim 
125*2810ac1bSKiyoung Kim     conf->ai_family = PF_UNSPEC;
126*2810ac1bSKiyoung Kim     conf->ai_socktype = SOCK_STREAM;
127*2810ac1bSKiyoung Kim     conf->ai_protocol = 0;
128*2810ac1bSKiyoung Kim     conf->ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
129*2810ac1bSKiyoung Kim 
130*2810ac1bSKiyoung Kim     err = getaddrinfo(hostname, "80", conf, &detail);
131*2810ac1bSKiyoung Kim     if (err != 0) {
132*2810ac1bSKiyoung Kim 	goto done;
133*2810ac1bSKiyoung Kim     }
134*2810ac1bSKiyoung Kim 
135*2810ac1bSKiyoung Kim     ret = socket(detail->ai_family, detail->ai_socktype, detail->ai_protocol);
136*2810ac1bSKiyoung Kim     if (ret == -1) {
137*2810ac1bSKiyoung Kim 	goto done;
138*2810ac1bSKiyoung Kim     }
139*2810ac1bSKiyoung Kim 
140*2810ac1bSKiyoung Kim     if (setsockopt(ret, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) {
141*2810ac1bSKiyoung Kim 	close(ret);
142*2810ac1bSKiyoung Kim 	ret = -1;
143*2810ac1bSKiyoung Kim 	goto done;
144*2810ac1bSKiyoung Kim     }
145*2810ac1bSKiyoung Kim 
146*2810ac1bSKiyoung Kim     if (bind(ret, detail->ai_addr, detail->ai_addrlen)) {
147*2810ac1bSKiyoung Kim 	close(ret);
148*2810ac1bSKiyoung Kim 	ret = -1;
149*2810ac1bSKiyoung Kim 	goto done;
150*2810ac1bSKiyoung Kim     }
151*2810ac1bSKiyoung Kim 
152*2810ac1bSKiyoung Kim  done:
153*2810ac1bSKiyoung Kim     if (detail != NULL) {
154*2810ac1bSKiyoung Kim 	freeaddrinfo(detail);
155*2810ac1bSKiyoung Kim     }
156*2810ac1bSKiyoung Kim     free(conf);
157*2810ac1bSKiyoung Kim 
158*2810ac1bSKiyoung Kim     return ret;
159*2810ac1bSKiyoung Kim }
160*2810ac1bSKiyoung Kim 
161*2810ac1bSKiyoung Kim /*
162*2810ac1bSKiyoung Kim  * set_fd3 forces file descriptor 3 to be associated with a unix
163*2810ac1bSKiyoung Kim  * socket that can be used to send a file descriptor back to the
164*2810ac1bSKiyoung Kim  * parent program.
165*2810ac1bSKiyoung Kim  */
set_fd3(void * detail)166*2810ac1bSKiyoung Kim static int set_fd3(void *detail)
167*2810ac1bSKiyoung Kim {
168*2810ac1bSKiyoung Kim     int *sp = detail;
169*2810ac1bSKiyoung Kim 
170*2810ac1bSKiyoung Kim     close(sp[0]);
171*2810ac1bSKiyoung Kim     if (dup2(sp[1], 3) != 3) {
172*2810ac1bSKiyoung Kim 	return -1;
173*2810ac1bSKiyoung Kim     }
174*2810ac1bSKiyoung Kim     close(sp[1]);
175*2810ac1bSKiyoung Kim 
176*2810ac1bSKiyoung Kim     return 0;
177*2810ac1bSKiyoung Kim }
178*2810ac1bSKiyoung Kim 
179*2810ac1bSKiyoung Kim /*
180*2810ac1bSKiyoung Kim  * bind80 returns a socket filedescriptor that is bound to port 80 of
181*2810ac1bSKiyoung Kim  * the provided service address.
182*2810ac1bSKiyoung Kim  *
183*2810ac1bSKiyoung Kim  * Example:
184*2810ac1bSKiyoung Kim  *
185*2810ac1bSKiyoung Kim  *   int fd = bind80("localhost");
186*2810ac1bSKiyoung Kim  *
187*2810ac1bSKiyoung Kim  * fd < 0 in the case of error.
188*2810ac1bSKiyoung Kim  */
bind80(const char * hostname)189*2810ac1bSKiyoung Kim int bind80(const char *hostname)
190*2810ac1bSKiyoung Kim {
191*2810ac1bSKiyoung Kim     cap_launch_t helper;
192*2810ac1bSKiyoung Kim     pid_t child;
193*2810ac1bSKiyoung Kim     char const *args[3];
194*2810ac1bSKiyoung Kim     char *path;
195*2810ac1bSKiyoung Kim     int fd, ignored;
196*2810ac1bSKiyoung Kim     int sp[2];
197*2810ac1bSKiyoung Kim     char junk[1];
198*2810ac1bSKiyoung Kim     const int rec_buf_len = CMSG_SPACE(sizeof(int));
199*2810ac1bSKiyoung Kim     char *rec_buf[CMSG_SPACE(sizeof(int))];
200*2810ac1bSKiyoung Kim     struct iovec *iov;
201*2810ac1bSKiyoung Kim     struct msghdr *msg;
202*2810ac1bSKiyoung Kim 
203*2810ac1bSKiyoung Kim     fd = try_bind80(hostname);
204*2810ac1bSKiyoung Kim     if (fd >= 0) {
205*2810ac1bSKiyoung Kim 	return fd;
206*2810ac1bSKiyoung Kim     }
207*2810ac1bSKiyoung Kim 
208*2810ac1bSKiyoung Kim #ifdef CAPSO_DEBUG
209*2810ac1bSKiyoung Kim     printf("application bind80(%s) attempt failed\n", hostname);
210*2810ac1bSKiyoung Kim     sleep(30);
211*2810ac1bSKiyoung Kim #endif
212*2810ac1bSKiyoung Kim 
213*2810ac1bSKiyoung Kim     iov = calloc(1, sizeof(struct iovec));
214*2810ac1bSKiyoung Kim     if (iov == NULL) {
215*2810ac1bSKiyoung Kim       return -1;
216*2810ac1bSKiyoung Kim     }
217*2810ac1bSKiyoung Kim     msg = calloc(1, sizeof(struct msghdr));
218*2810ac1bSKiyoung Kim     if (msg == NULL) {
219*2810ac1bSKiyoung Kim       free(iov);
220*2810ac1bSKiyoung Kim       return -1;
221*2810ac1bSKiyoung Kim     }
222*2810ac1bSKiyoung Kim 
223*2810ac1bSKiyoung Kim     /*
224*2810ac1bSKiyoung Kim      * Initial attempt didn't work, so try launching the shared
225*2810ac1bSKiyoung Kim      * library as an executable and getting it to yield a bound
226*2810ac1bSKiyoung Kim      * filedescriptor for us via a unix socket pair.
227*2810ac1bSKiyoung Kim      */
228*2810ac1bSKiyoung Kim     path = where_am_i();
229*2810ac1bSKiyoung Kim     if (path == NULL) {
230*2810ac1bSKiyoung Kim 	perror("Unable to find self");
231*2810ac1bSKiyoung Kim 	goto drop_alloc;
232*2810ac1bSKiyoung Kim     }
233*2810ac1bSKiyoung Kim 
234*2810ac1bSKiyoung Kim     args[0] = "bind80-helper";
235*2810ac1bSKiyoung Kim     args[1] = hostname;
236*2810ac1bSKiyoung Kim     args[2] = NULL;
237*2810ac1bSKiyoung Kim 
238*2810ac1bSKiyoung Kim     helper = cap_new_launcher(path, args, (void *) environ);
239*2810ac1bSKiyoung Kim     if (helper == NULL) {
240*2810ac1bSKiyoung Kim 	goto drop_path;
241*2810ac1bSKiyoung Kim     }
242*2810ac1bSKiyoung Kim 
243*2810ac1bSKiyoung Kim     if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sp)) {
244*2810ac1bSKiyoung Kim 	goto drop_helper;
245*2810ac1bSKiyoung Kim     }
246*2810ac1bSKiyoung Kim 
247*2810ac1bSKiyoung Kim     cap_launcher_callback(helper, set_fd3);
248*2810ac1bSKiyoung Kim     child = cap_launch(helper, sp);
249*2810ac1bSKiyoung Kim     close(sp[1]);
250*2810ac1bSKiyoung Kim 
251*2810ac1bSKiyoung Kim     if (child <= 0) {
252*2810ac1bSKiyoung Kim 	goto drop_sp;
253*2810ac1bSKiyoung Kim     }
254*2810ac1bSKiyoung Kim 
255*2810ac1bSKiyoung Kim     iov[0].iov_base = junk;
256*2810ac1bSKiyoung Kim     iov[0].iov_len = 1;
257*2810ac1bSKiyoung Kim 
258*2810ac1bSKiyoung Kim     msg->msg_name = NULL;
259*2810ac1bSKiyoung Kim     msg->msg_namelen = 0;
260*2810ac1bSKiyoung Kim     msg->msg_iov = iov;
261*2810ac1bSKiyoung Kim     msg->msg_iovlen = 1;
262*2810ac1bSKiyoung Kim     msg->msg_control = rec_buf;
263*2810ac1bSKiyoung Kim     msg->msg_controllen = rec_buf_len;
264*2810ac1bSKiyoung Kim 
265*2810ac1bSKiyoung Kim     if (recvmsg(sp[0], msg, 0) != -1) {
266*2810ac1bSKiyoung Kim 	fd = * (int *) CMSG_DATA(CMSG_FIRSTHDR(msg));
267*2810ac1bSKiyoung Kim     }
268*2810ac1bSKiyoung Kim     waitpid(child, &ignored, 0);
269*2810ac1bSKiyoung Kim 
270*2810ac1bSKiyoung Kim  drop_sp:
271*2810ac1bSKiyoung Kim     close(sp[0]);
272*2810ac1bSKiyoung Kim 
273*2810ac1bSKiyoung Kim  drop_helper:
274*2810ac1bSKiyoung Kim     cap_free(helper);
275*2810ac1bSKiyoung Kim 
276*2810ac1bSKiyoung Kim  drop_path:
277*2810ac1bSKiyoung Kim     free(path);
278*2810ac1bSKiyoung Kim 
279*2810ac1bSKiyoung Kim  drop_alloc:
280*2810ac1bSKiyoung Kim     free(msg);
281*2810ac1bSKiyoung Kim     free(iov);
282*2810ac1bSKiyoung Kim 
283*2810ac1bSKiyoung Kim     return fd;
284*2810ac1bSKiyoung Kim }
285*2810ac1bSKiyoung Kim 
286*2810ac1bSKiyoung Kim #include "../../libcap/execable.h"
287*2810ac1bSKiyoung Kim //#define SO_MAIN int main
288*2810ac1bSKiyoung Kim 
SO_MAIN(int argc,char ** argv)289*2810ac1bSKiyoung Kim SO_MAIN(int argc, char **argv)
290*2810ac1bSKiyoung Kim {
291*2810ac1bSKiyoung Kim     const char *cmd = "<capso.so>";
292*2810ac1bSKiyoung Kim     const cap_value_t cap_net_bind_service = CAP_NET_BIND_SERVICE;
293*2810ac1bSKiyoung Kim     cap_t working;
294*2810ac1bSKiyoung Kim     int fd;
295*2810ac1bSKiyoung Kim     struct msghdr msg;
296*2810ac1bSKiyoung Kim     struct cmsghdr *ctrl;
297*2810ac1bSKiyoung Kim     struct iovec payload;
298*2810ac1bSKiyoung Kim     char data[CMSG_SPACE(sizeof(fd))];
299*2810ac1bSKiyoung Kim     char junk[1];
300*2810ac1bSKiyoung Kim 
301*2810ac1bSKiyoung Kim #ifdef CAPSO_DEBUG
302*2810ac1bSKiyoung Kim     printf("invoking %s standalone\n", argv[0]);
303*2810ac1bSKiyoung Kim     sleep(30);
304*2810ac1bSKiyoung Kim #endif
305*2810ac1bSKiyoung Kim 
306*2810ac1bSKiyoung Kim     if (argv != NULL) {
307*2810ac1bSKiyoung Kim 	cmd = argv[0];
308*2810ac1bSKiyoung Kim     }
309*2810ac1bSKiyoung Kim 
310*2810ac1bSKiyoung Kim     if (argc != 2 || argv[1] == NULL || !strcmp(argv[1], "--help")) {
311*2810ac1bSKiyoung Kim 	fprintf(stderr, "usage: %s <hostname>\n", cmd);
312*2810ac1bSKiyoung Kim 	exit(1);
313*2810ac1bSKiyoung Kim     }
314*2810ac1bSKiyoung Kim 
315*2810ac1bSKiyoung Kim     working = cap_get_proc();
316*2810ac1bSKiyoung Kim     if (working == NULL) {
317*2810ac1bSKiyoung Kim 	perror("Unable to read capabilities");
318*2810ac1bSKiyoung Kim 	exit(1);
319*2810ac1bSKiyoung Kim     }
320*2810ac1bSKiyoung Kim 
321*2810ac1bSKiyoung Kim     if (cap_set_flag(working, CAP_EFFECTIVE, 1,
322*2810ac1bSKiyoung Kim 		     &cap_net_bind_service, CAP_SET) != 0) {
323*2810ac1bSKiyoung Kim 	perror("Unable to raise CAP_NET_BIND_SERVICE");
324*2810ac1bSKiyoung Kim 	exit(1);
325*2810ac1bSKiyoung Kim     }
326*2810ac1bSKiyoung Kim 
327*2810ac1bSKiyoung Kim     if (cap_set_proc(working) != 0) {
328*2810ac1bSKiyoung Kim 	perror("Problem with cap_set_proc");
329*2810ac1bSKiyoung Kim 	fprintf(stderr, "Try: sudo setcap cap_net_bind_service=p %s\n",
330*2810ac1bSKiyoung Kim 		argv[0]);
331*2810ac1bSKiyoung Kim 	exit(1);
332*2810ac1bSKiyoung Kim     }
333*2810ac1bSKiyoung Kim 
334*2810ac1bSKiyoung Kim     fd = try_bind80(argv[1]);
335*2810ac1bSKiyoung Kim 
336*2810ac1bSKiyoung Kim     memset(data, 0, sizeof(data));
337*2810ac1bSKiyoung Kim     memset(&payload, 0, sizeof(payload));
338*2810ac1bSKiyoung Kim 
339*2810ac1bSKiyoung Kim     payload.iov_base = junk;
340*2810ac1bSKiyoung Kim     payload.iov_len = 1;
341*2810ac1bSKiyoung Kim 
342*2810ac1bSKiyoung Kim     msg.msg_name = NULL;
343*2810ac1bSKiyoung Kim     msg.msg_namelen = 0;
344*2810ac1bSKiyoung Kim     msg.msg_iov = &payload;
345*2810ac1bSKiyoung Kim     msg.msg_iovlen = 1;
346*2810ac1bSKiyoung Kim     msg.msg_control = data;
347*2810ac1bSKiyoung Kim     msg.msg_controllen = sizeof(data);
348*2810ac1bSKiyoung Kim 
349*2810ac1bSKiyoung Kim     ctrl = CMSG_FIRSTHDR(&msg);
350*2810ac1bSKiyoung Kim     ctrl->cmsg_level = SOL_SOCKET;
351*2810ac1bSKiyoung Kim     ctrl->cmsg_type = SCM_RIGHTS;
352*2810ac1bSKiyoung Kim     ctrl->cmsg_len = CMSG_LEN(sizeof(fd));
353*2810ac1bSKiyoung Kim 
354*2810ac1bSKiyoung Kim     *((int *) CMSG_DATA(ctrl)) = fd;
355*2810ac1bSKiyoung Kim 
356*2810ac1bSKiyoung Kim     if (sendmsg(3, &msg, 0) < 0) {
357*2810ac1bSKiyoung Kim 	perror("Failed to write fd");
358*2810ac1bSKiyoung Kim     }
359*2810ac1bSKiyoung Kim 
360*2810ac1bSKiyoung Kim     fake_exploit();
361*2810ac1bSKiyoung Kim 
362*2810ac1bSKiyoung Kim #ifdef CAPSO_DEBUG
363*2810ac1bSKiyoung Kim     printf("exiting standalone %s\n", argv[0]);
364*2810ac1bSKiyoung Kim     sleep(30);
365*2810ac1bSKiyoung Kim #endif
366*2810ac1bSKiyoung Kim 
367*2810ac1bSKiyoung Kim     exit(0);
368*2810ac1bSKiyoung Kim }
369