* initial contribution for 2.0 beta (0.0.2)
[framework/system/sdbd.git] / src / services.c
1 /*
2  * Copyright (C) 2007 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include <unistd.h>
20 #include <string.h>
21 #include <errno.h>
22
23 #include "sysdeps.h"
24
25 #define  TRACE_TAG  TRACE_SDB
26 #include "sdb.h"
27 #include "file_sync_service.h"
28
29 #if SDB_HOST
30 #  ifndef HAVE_WINSOCK
31 #    include <netinet/in.h>
32 #    include <netdb.h>
33 #  endif
34 #else
35 //#  include <sys/reboot.h> eric
36 #endif
37
38 #define PROCESS_WORKING_DIRECTORY "/"
39
40 typedef struct stinfo stinfo;
41
42 struct stinfo {
43     void (*func)(int fd, void *cookie);
44     int fd;
45     void *cookie;
46 };
47
48
49 void *service_bootstrap_func(void *x)
50 {
51     stinfo *sti = x;
52     sti->func(sti->fd, sti->cookie);
53     free(sti);
54     return 0;
55 }
56
57 #if SDB_HOST
58 SDB_MUTEX_DEFINE( dns_lock );
59
60 static void dns_service(int fd, void *cookie)
61 {
62     char *hostname = cookie;
63     struct hostent *hp;
64     unsigned zero = 0;
65
66     sdb_mutex_lock(&dns_lock);
67     hp = gethostbyname(hostname);
68     free(cookie);
69     if(hp == 0) {
70         writex(fd, &zero, 4);
71     } else {
72         writex(fd, hp->h_addr, 4);
73     }
74     sdb_mutex_unlock(&dns_lock);
75     sdb_close(fd);
76 }
77 #else
78 extern int recovery_mode;
79
80 static void recover_service(int s, void *cookie)
81 {
82     unsigned char buf[4096];
83     unsigned count = (unsigned) cookie;
84     int fd;
85
86     fd = sdb_creat("/tmp/update", 0644);
87     if(fd < 0) {
88         sdb_close(s);
89         return;
90     }
91
92     while(count > 0) {
93         unsigned xfer = (count > 4096) ? 4096 : count;
94         if(readx(s, buf, xfer)) break;
95         if(writex(fd, buf, xfer)) break;
96         count -= xfer;
97     }
98
99     if(count == 0) {
100         writex(s, "OKAY", 4);
101     } else {
102         writex(s, "FAIL", 4);
103     }
104     sdb_close(fd);
105     sdb_close(s);
106
107     fd = sdb_creat("/tmp/update.begin", 0644);
108     sdb_close(fd);
109 }
110 #if 0 //eric
111 void restart_root_service(int fd, void *cookie)
112 {
113     char buf[100];
114     char value[PROPERTY_VALUE_MAX];
115
116     if (getuid() == 0) {
117         snprintf(buf, sizeof(buf), "sdbd is already running as root\n");
118         writex(fd, buf, strlen(buf));
119         sdb_close(fd);
120     } else {
121         property_get("ro.debuggable", value, "");
122         if (strcmp(value, "1") != 0) {
123             snprintf(buf, sizeof(buf), "sdbd cannot run as root in production builds\n");
124             writex(fd, buf, strlen(buf));
125             sdb_close(fd);
126             return;
127         }
128
129         property_set("service.sdb.root", "1");
130         snprintf(buf, sizeof(buf), "restarting sdbd as root\n");
131         writex(fd, buf, strlen(buf));
132         sdb_close(fd);
133
134         // quit, and init will restart us as root
135         sleep(1);
136         exit(1);
137     }
138 }
139
140 void restart_tcp_service(int fd, void *cookie)
141 {
142     char buf[100];
143     char value[PROPERTY_VALUE_MAX];
144     int port = (int)cookie;
145
146     if (port <= 0) {
147         snprintf(buf, sizeof(buf), "invalid port\n");
148         writex(fd, buf, strlen(buf));
149         sdb_close(fd);
150         return;
151     }
152
153     snprintf(value, sizeof(value), "%d", port);
154     property_set("service.sdb.tcp.port", value);
155     snprintf(buf, sizeof(buf), "restarting in TCP mode port: %d\n", port);
156     writex(fd, buf, strlen(buf));
157     sdb_close(fd);
158
159     // quit, and init will restart us in TCP mode
160     sleep(1);
161     exit(1);
162 }
163
164 void restart_usb_service(int fd, void *cookie)
165 {
166     char buf[100];
167
168     property_set("service.sdb.tcp.port", "0");
169     snprintf(buf, sizeof(buf), "restarting in USB mode\n");
170     writex(fd, buf, strlen(buf));
171     sdb_close(fd);
172
173     // quit, and init will restart us in USB mode
174     sleep(1);
175     exit(1);
176 }
177
178 #endif
179 void reboot_service(int fd, void *arg)
180 {
181     char buf[100];
182     int pid, ret;
183
184     sync();
185
186     /* Attempt to unmount the SD card first.
187      * No need to bother checking for errors.
188      */
189     pid = fork();
190     if (pid == 0) {
191         /* ask vdc to unmount it */
192         execl("/system/bin/vdc", "/system/bin/vdc", "volume", "unmount",
193                 getenv("EXTERNAL_STORAGE"), "force", NULL);
194     } else if (pid > 0) {
195         /* wait until vdc succeeds or fails */
196         waitpid(pid, &ret, 0);
197     }
198
199 //    ret = __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
200 //                    LINUX_REBOOT_CMD_RESTART2, (char *)arg);
201     if (ret < 0) {
202         snprintf(buf, sizeof(buf), "reboot failed: %s\n", strerror(errno));
203         writex(fd, buf, strlen(buf));
204     }
205     free(arg);
206     sdb_close(fd);
207 }
208
209 #endif
210
211 #if 0
212 static void echo_service(int fd, void *cookie)
213 {
214     char buf[4096];
215     int r;
216     char *p;
217     int c;
218
219     for(;;) {
220         r = read(fd, buf, 4096);
221         if(r == 0) goto done;
222         if(r < 0) {
223             if(errno == EINTR) continue;
224             else goto done;
225         }
226
227         c = r;
228         p = buf;
229         while(c > 0) {
230             r = write(fd, p, c);
231             if(r > 0) {
232                 c -= r;
233                 p += r;
234                 continue;
235             }
236             if((r < 0) && (errno == EINTR)) continue;
237             goto done;
238         }
239     }
240 done:
241     close(fd);
242 }
243 #endif
244
245 static int create_service_thread(void (*func)(int, void *), void *cookie)
246 {
247     stinfo *sti;
248     sdb_thread_t t;
249     int s[2];
250
251     if(sdb_socketpair(s)) {
252         printf("cannot create service socket pair\n");
253         return -1;
254     }
255
256     sti = malloc(sizeof(stinfo));
257     if(sti == 0) fatal("cannot allocate stinfo");
258     sti->func = func;
259     sti->cookie = cookie;
260     sti->fd = s[1];
261
262     if(sdb_thread_create( &t, service_bootstrap_func, sti)){
263         free(sti);
264         sdb_close(s[0]);
265         sdb_close(s[1]);
266         printf("cannot create service thread\n");
267         return -1;
268     }
269
270     D("service thread started, %d:%d\n",s[0], s[1]);
271     return s[0];
272 }
273
274 static int create_subprocess(const char *cmd, const char *arg0, const char *arg1)
275 {
276 #ifdef HAVE_WIN32_PROC
277         fprintf(stderr, "error: create_subprocess not implemented on Win32 (%s %s %s)\n", cmd, arg0, arg1);
278         return -1;
279 #else /* !HAVE_WIN32_PROC */
280     char *devname;
281     int ptm;
282     pid_t pid;
283
284     ptm = unix_open("/dev/ptmx", O_RDWR); // | O_NOCTTY);
285     if(ptm < 0){
286         printf("[ cannot open /dev/ptmx - %s ]\n",strerror(errno));
287         return -1;
288     }
289     fcntl(ptm, F_SETFD, FD_CLOEXEC);
290
291     if(grantpt(ptm) || unlockpt(ptm) ||
292        ((devname = (char*) ptsname(ptm)) == 0)){
293         printf("[ trouble with /dev/ptmx - %s ]\n", strerror(errno));
294         return -1;
295     }
296
297     pid = fork();
298     if(pid < 0) {
299         printf("- fork failed: %s -\n", strerror(errno));
300         return -1;
301     }
302
303     if(pid == 0){
304         int pts;
305
306         setsid();
307         if (chdir(PROCESS_WORKING_DIRECTORY) < 0)
308                 D("sdb: unable to change working directory to %s\n", PROCESS_WORKING_DIRECTORY);
309
310         pts = unix_open(devname, O_RDWR);
311         if(pts < 0) exit(-1);
312
313         dup2(pts, 0);
314         dup2(pts, 1);
315         dup2(pts, 2);
316
317         sdb_close(ptm);
318
319         execl(cmd, cmd, arg0, arg1, NULL);
320         fprintf(stderr, "- exec '%s' failed: %s (%d) -\n",
321                 cmd, strerror(errno), errno);
322         exit(-1);
323     } else {
324 #if !SDB_HOST
325         // set child's OOM adjustment to zero
326         char text[64];
327         snprintf(text, sizeof text, "/proc/%d/oom_adj", pid);
328         int fd = sdb_open(text, O_WRONLY);
329         if (fd >= 0) {
330             sdb_write(fd, "0", 1);
331             sdb_close(fd);
332         } else {
333            D("sdb: unable to open %s\n", text);
334         }
335 #endif
336         return ptm;
337     }
338 #endif /* !HAVE_WIN32_PROC */
339 }
340
341 //#if SDB_HOST
342 #define SHELL_COMMAND "/bin/sh"
343 //#else
344 //#define SHELL_COMMAND "/system/bin/sh"
345 //#endif
346
347 int service_to_fd(const char *name)
348 {
349     int ret = -1;
350
351     if(!strncmp(name, "tcp:", 4)) {
352         int port = atoi(name + 4);
353         name = strchr(name + 4, ':');
354         if(name == 0) {
355             ret = socket_loopback_client(port, SOCK_STREAM);
356             if (ret >= 0)
357                 disable_tcp_nagle(ret);
358         } else {
359 #if SDB_HOST
360             sdb_mutex_lock(&dns_lock);
361             ret = socket_network_client(name + 1, port, SOCK_STREAM);
362             sdb_mutex_unlock(&dns_lock);
363 #else
364             return -1;
365 #endif
366         }
367 #ifndef HAVE_WINSOCK   /* winsock doesn't implement unix domain sockets */
368     } else if(!strncmp(name, "local:", 6)) {
369         ret = socket_local_client(name + 6,
370                 ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM);
371     } else if(!strncmp(name, "localreserved:", 14)) {
372         ret = socket_local_client(name + 14,
373                 ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM);
374     } else if(!strncmp(name, "localabstract:", 14)) {
375         ret = socket_local_client(name + 14,
376                 ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
377     } else if(!strncmp(name, "localfilesystem:", 16)) {
378         ret = socket_local_client(name + 16,
379                 ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM);
380 #endif
381 #if SDB_HOST
382     } else if(!strncmp("dns:", name, 4)){
383         char *n = strdup(name + 4);
384         if(n == 0) return -1;
385         ret = create_service_thread(dns_service, n);
386 #else /* !SDB_HOST */
387     } else if(!strncmp("dev:", name, 4)) {
388         ret = unix_open(name + 4, O_RDWR);
389     } else if(!strncmp(name, "framebuffer:", 12)) {
390         ret = create_service_thread(framebuffer_service, 0);
391     } else if(recovery_mode && !strncmp(name, "recover:", 8)) {
392         ret = create_service_thread(recover_service, (void*) atoi(name + 8));
393     } else if (!strncmp(name, "jdwp:", 5)) {
394         ret = create_jdwp_connection_fd(atoi(name+5));
395 #if 0   // eric
396     } else if (!strncmp(name, "log:", 4)) {
397         ret = create_service_thread(log_service, get_log_file_path(name + 4));
398 #endif
399 #endif
400     } else if(!HOST && !strncmp(name, "shell:", 6)) {
401         if(name[6]) {
402             ret = create_subprocess(SHELL_COMMAND, "-c", name + 6);
403         } else {
404             ret = create_subprocess(SHELL_COMMAND, "-", 0);
405         }
406 #if !SDB_HOST
407     } else if(!strncmp(name, "sync:", 5)) {
408         ret = create_service_thread(file_sync_service, NULL);
409 #if 0 //eric
410     } else if(!strncmp(name, "remount:", 8)) {
411         ret = create_service_thread(remount_service, NULL);
412 #endif
413     } else if(!strncmp(name, "reboot:", 7)) {
414         void* arg = strdup(name + 7);
415         if(arg == 0) return -1;
416         ret = create_service_thread(reboot_service, arg);
417 #if 0 //eric
418     } else if(!strncmp(name, "root:", 5)) {
419         ret = create_service_thread(restart_root_service, NULL);
420     } else if(!strncmp(name, "tcpip:", 6)) {
421         int port;
422         if (sscanf(name + 6, "%d", &port) == 0) {
423             port = 0;
424         }
425         ret = create_service_thread(restart_tcp_service, (void *)port);
426     } else if(!strncmp(name, "usb:", 4)) {
427         ret = create_service_thread(restart_usb_service, NULL);
428 #endif
429 #endif
430 #if 0
431     } else if(!strncmp(name, "echo:", 5)){
432         ret = create_service_thread(echo_service, 0);
433 #endif
434     }
435     if (ret >= 0) {
436         close_on_exec(ret);
437     }
438     return ret;
439 }
440
441 #if SDB_HOST
442 struct state_info {
443     transport_type transport;
444     char* serial;
445     int state;
446 };
447
448 static void wait_for_state(int fd, void* cookie)
449 {
450     struct state_info* sinfo = cookie;
451     char* err = "unknown error";
452
453     D("wait_for_state %d\n", sinfo->state);
454
455     atransport *t = acquire_one_transport(sinfo->state, sinfo->transport, sinfo->serial, &err);
456     if(t != 0) {
457         writex(fd, "OKAY", 4);
458     } else {
459         sendfailmsg(fd, err);
460     }
461
462     if (sinfo->serial)
463         free(sinfo->serial);
464     free(sinfo);
465     sdb_close(fd);
466     D("wait_for_state is done\n");
467 }
468 #endif
469
470 #if SDB_HOST
471 asocket*  host_service_to_socket(const char*  name, const char *serial)
472 {
473     if (!strcmp(name,"track-devices")) {
474         return create_device_tracker();
475     } else if (!strncmp(name, "wait-for-", strlen("wait-for-"))) {
476         struct state_info* sinfo = malloc(sizeof(struct state_info));
477
478         if (serial)
479             sinfo->serial = strdup(serial);
480         else
481             sinfo->serial = NULL;
482
483         name += strlen("wait-for-");
484
485         if (!strncmp(name, "local", strlen("local"))) {
486             sinfo->transport = kTransportLocal;
487             sinfo->state = CS_DEVICE;
488         } else if (!strncmp(name, "usb", strlen("usb"))) {
489             sinfo->transport = kTransportUsb;
490             sinfo->state = CS_DEVICE;
491         } else if (!strncmp(name, "any", strlen("any"))) {
492             sinfo->transport = kTransportAny;
493             sinfo->state = CS_DEVICE;
494         } else {
495             free(sinfo);
496             return NULL;
497         }
498
499         int fd = create_service_thread(wait_for_state, sinfo);
500         return create_local_socket(fd);
501     }
502     return NULL;
503 }
504 #endif /* SDB_HOST */