documentation convert to doxygen
[platform/upstream/libwebsockets.git] / lib / lws-plat-unix.c
1 #include "private-libwebsockets.h"
2
3 #include <pwd.h>
4 #include <grp.h>
5
6 #include <dlfcn.h>
7 #include <dirent.h>
8
9
10 /*
11  * included from libwebsockets.c for unix builds
12  */
13
14 unsigned long long time_in_microseconds(void)
15 {
16         struct timeval tv;
17         gettimeofday(&tv, NULL);
18         return ((unsigned long long)tv.tv_sec * 1000000LL) + tv.tv_usec;
19 }
20
21 LWS_VISIBLE int
22 lws_get_random(struct lws_context *context, void *buf, int len)
23 {
24         return read(context->fd_random, (char *)buf, len);
25 }
26
27 LWS_VISIBLE int
28 lws_send_pipe_choked(struct lws *wsi)
29 {
30         struct lws_pollfd fds;
31
32         /* treat the fact we got a truncated send pending as if we're choked */
33         if (wsi->trunc_len)
34                 return 1;
35
36         fds.fd = wsi->sock;
37         fds.events = POLLOUT;
38         fds.revents = 0;
39
40         if (poll(&fds, 1, 0) != 1)
41                 return 1;
42
43         if ((fds.revents & POLLOUT) == 0)
44                 return 1;
45
46         /* okay to send another packet without blocking */
47
48         return 0;
49 }
50
51 LWS_VISIBLE int
52 lws_poll_listen_fd(struct lws_pollfd *fd)
53 {
54         return poll(fd, 1, 0);
55 }
56
57 LWS_VISIBLE void
58 lws_cancel_service_pt(struct lws *wsi)
59 {
60         struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
61         char buf = 0;
62
63         if (write(pt->dummy_pipe_fds[1], &buf, sizeof(buf)) != 1)
64                 lwsl_err("Cannot write to dummy pipe");
65 }
66
67 LWS_VISIBLE void
68 lws_cancel_service(struct lws_context *context)
69 {
70         struct lws_context_per_thread *pt = &context->pt[0];
71         char buf = 0, m = context->count_threads;
72
73         while (m--) {
74                 if (write(pt->dummy_pipe_fds[1], &buf, sizeof(buf)) != 1)
75                         lwsl_err("Cannot write to dummy pipe");
76                 pt++;
77         }
78 }
79
80 LWS_VISIBLE void lwsl_emit_syslog(int level, const char *line)
81 {
82         int syslog_level = LOG_DEBUG;
83
84         switch (level) {
85         case LLL_ERR:
86                 syslog_level = LOG_ERR;
87                 break;
88         case LLL_WARN:
89                 syslog_level = LOG_WARNING;
90                 break;
91         case LLL_NOTICE:
92                 syslog_level = LOG_NOTICE;
93                 break;
94         case LLL_INFO:
95                 syslog_level = LOG_INFO;
96                 break;
97         }
98         syslog(syslog_level, "%s", line);
99 }
100
101 LWS_VISIBLE int
102 lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
103 {
104         struct lws_context_per_thread *pt = &context->pt[tsi];
105         int n = -1, m, c;
106         char buf;
107
108         /* stay dead once we are dead */
109
110         if (!context || !context->vhost_list)
111                 return 1;
112
113         if (timeout_ms < 0)
114                 goto faked_service;
115
116         lws_libev_run(context, tsi);
117         lws_libuv_run(context, tsi);
118
119         if (!context->service_tid_detected) {
120                 struct lws _lws;
121
122                 memset(&_lws, 0, sizeof(_lws));
123                 _lws.context = context;
124
125                 context->service_tid_detected =
126                         context->vhost_list->protocols[0].callback(
127                         &_lws, LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0);
128         }
129         context->service_tid = context->service_tid_detected;
130
131         timeout_ms = lws_service_adjust_timeout(context, timeout_ms, tsi);
132
133         n = poll(pt->fds, pt->fds_count, timeout_ms);
134
135 #ifdef LWS_OPENSSL_SUPPORT
136         if (!pt->rx_draining_ext_list &&
137             !lws_ssl_anybody_has_buffered_read_tsi(context, tsi) && !n) {
138 #else
139         if (!pt->rx_draining_ext_list && !n) /* poll timeout */ {
140 #endif
141                 lws_service_fd_tsi(context, NULL, tsi);
142                 return 0;
143         }
144
145 faked_service:
146         m = lws_service_flag_pending(context, tsi);
147         if (m)
148                 c = -1; /* unknown limit */
149         else
150                 if (n < 0) {
151                         if (LWS_ERRNO != LWS_EINTR)
152                                 return -1;
153                         return 0;
154                 } else
155                         c = n;
156
157         /* any socket with events to service? */
158         for (n = 0; n < pt->fds_count && c; n++) {
159                 if (!pt->fds[n].revents)
160                         continue;
161
162                 c--;
163
164                 if (pt->fds[n].fd == pt->dummy_pipe_fds[0]) {
165                         if (read(pt->fds[n].fd, &buf, 1) != 1)
166                                 lwsl_err("Cannot read from dummy pipe.");
167                         continue;
168                 }
169
170                 m = lws_service_fd_tsi(context, &pt->fds[n], tsi);
171                 if (m < 0)
172                         return -1;
173                 /* if something closed, retry this slot */
174                 if (m)
175                         n--;
176         }
177
178         return 0;
179 }
180
181 LWS_VISIBLE int
182 lws_plat_service(struct lws_context *context, int timeout_ms)
183 {
184         return lws_plat_service_tsi(context, timeout_ms, 0);
185 }
186
187 LWS_VISIBLE int
188 lws_plat_set_socket_options(struct lws_vhost *vhost, int fd)
189 {
190         int optval = 1;
191         socklen_t optlen = sizeof(optval);
192
193 #if defined(__APPLE__) || \
194     defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \
195     defined(__NetBSD__) || \
196     defined(__OpenBSD__)
197         struct protoent *tcp_proto;
198 #endif
199
200         if (vhost->ka_time) {
201                 /* enable keepalive on this socket */
202                 optval = 1;
203                 if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE,
204                                (const void *)&optval, optlen) < 0)
205                         return 1;
206
207 #if defined(__APPLE__) || \
208     defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \
209     defined(__NetBSD__) || \
210         defined(__CYGWIN__) || defined(__OpenBSD__)
211
212                 /*
213                  * didn't find a way to set these per-socket, need to
214                  * tune kernel systemwide values
215                  */
216 #else
217                 /* set the keepalive conditions we want on it too */
218                 optval = vhost->ka_time;
219                 if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE,
220                                (const void *)&optval, optlen) < 0)
221                         return 1;
222
223                 optval = vhost->ka_interval;
224                 if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL,
225                                (const void *)&optval, optlen) < 0)
226                         return 1;
227
228                 optval = vhost->ka_probes;
229                 if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT,
230                                (const void *)&optval, optlen) < 0)
231                         return 1;
232 #endif
233         }
234
235         /* Disable Nagle */
236         optval = 1;
237 #if !defined(__APPLE__) && \
238     !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__) && \
239     !defined(__NetBSD__) && \
240     !defined(__OpenBSD__)
241         if (setsockopt(fd, SOL_TCP, TCP_NODELAY, (const void *)&optval, optlen) < 0)
242                 return 1;
243 #else
244         tcp_proto = getprotobyname("TCP");
245         if (setsockopt(fd, tcp_proto->p_proto, TCP_NODELAY, &optval, optlen) < 0)
246                 return 1;
247 #endif
248
249         /* We are nonblocking... */
250         if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
251                 return 1;
252
253         return 0;
254 }
255
256 LWS_VISIBLE void
257 lws_plat_drop_app_privileges(struct lws_context_creation_info *info)
258 {
259         if (info->gid != -1)
260                 if (setgid(info->gid))
261                         lwsl_warn("setgid: %s\n", strerror(LWS_ERRNO));
262
263         if (info->uid != -1) {
264                 struct passwd *p = getpwuid(info->uid);
265
266                 if (p) {
267                         initgroups(p->pw_name, info->gid);
268                         if (setuid(info->uid))
269                                 lwsl_warn("setuid: %s\n", strerror(LWS_ERRNO));
270                         else
271                                 lwsl_notice("Set privs to user '%s'\n", p->pw_name);
272                 } else
273                         lwsl_warn("getpwuid: unable to find uid %d", info->uid);
274         }
275 }
276
277 #ifdef LWS_WITH_PLUGINS
278
279 #if defined(LWS_USE_LIBUV) && UV_VERSION_MAJOR > 0
280
281 /* libuv.c implements these in a cross-platform way */
282
283 #else
284
285 static int filter(const struct dirent *ent)
286 {
287         if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
288                 return 0;
289
290         return 1;
291 }
292
293 LWS_VISIBLE int
294 lws_plat_plugins_init(struct lws_context * context, const char * const *d)
295 {
296         struct lws_plugin_capability lcaps;
297         struct lws_plugin *plugin;
298         lws_plugin_init_func initfunc;
299         struct dirent **namelist;
300         int n, i, m, ret = 0;
301         char path[256];
302         void *l;
303
304         lwsl_notice("  Plugins:\n");
305
306         while (d && *d) {
307                 n = scandir(*d, &namelist, filter, alphasort);
308                 if (n < 0) {
309                         lwsl_err("Scandir on %s failed\n", *d);
310                         return 1;
311                 }
312
313                 for (i = 0; i < n; i++) {
314                         if (strlen(namelist[i]->d_name) < 7)
315                                 goto inval;
316
317                         lwsl_notice("   %s\n", namelist[i]->d_name);
318
319                         snprintf(path, sizeof(path) - 1, "%s/%s", *d,
320                                  namelist[i]->d_name);
321                         l = dlopen(path, RTLD_NOW);
322                         if (!l) {
323                                 lwsl_err("Error loading DSO: %s\n", dlerror());
324                                 while (i++ < n)
325                                         free(namelist[i]);
326                                 goto bail;
327                         }
328                         /* we could open it, can we get his init function? */
329                         m = snprintf(path, sizeof(path) - 1, "init_%s",
330                                      namelist[i]->d_name + 3 /* snip lib... */);
331                         path[m - 3] = '\0'; /* snip the .so */
332                         initfunc = dlsym(l, path);
333                         if (!initfunc) {
334                                 lwsl_err("Failed to get init on %s: %s",
335                                                 namelist[i]->d_name, dlerror());
336                                 dlclose(l);
337                         }
338                         lcaps.api_magic = LWS_PLUGIN_API_MAGIC;
339                         m = initfunc(context, &lcaps);
340                         if (m) {
341                                 lwsl_err("Initializing %s failed %d\n",
342                                         namelist[i]->d_name, m);
343                                 dlclose(l);
344                                 goto skip;
345                         }
346
347                         plugin = lws_malloc(sizeof(*plugin));
348                         if (!plugin) {
349                                 lwsl_err("OOM\n");
350                                 goto bail;
351                         }
352                         plugin->list = context->plugin_list;
353                         context->plugin_list = plugin;
354                         strncpy(plugin->name, namelist[i]->d_name, sizeof(plugin->name) - 1);
355                         plugin->name[sizeof(plugin->name) - 1] = '\0';
356                         plugin->l = l;
357                         plugin->caps = lcaps;
358                         context->plugin_protocol_count += lcaps.count_protocols;
359                         context->plugin_extension_count += lcaps.count_extensions;
360
361                         free(namelist[i]);
362                         continue;
363
364         skip:
365                         dlclose(l);
366         inval:
367                         free(namelist[i]);
368                 }
369                 free(namelist);
370                 d++;
371         }
372
373 bail:
374         free(namelist);
375
376         return ret;
377 }
378
379 LWS_VISIBLE int
380 lws_plat_plugins_destroy(struct lws_context * context)
381 {
382         struct lws_plugin *plugin = context->plugin_list, *p;
383         lws_plugin_destroy_func func;
384         char path[256];
385         int m;
386
387         if (!plugin)
388                 return 0;
389
390         lwsl_notice("%s\n", __func__);
391
392         while (plugin) {
393                 p = plugin;
394                 m = snprintf(path, sizeof(path) - 1, "destroy_%s", plugin->name + 3);
395                 path[m - 3] = '\0';
396                 func = dlsym(plugin->l, path);
397                 if (!func) {
398                         lwsl_err("Failed to get destroy on %s: %s",
399                                         plugin->name, dlerror());
400                         goto next;
401                 }
402                 m = func(context);
403                 if (m)
404                         lwsl_err("Initializing %s failed %d\n",
405                                 plugin->name, m);
406 next:
407                 dlclose(p->l);
408                 plugin = p->list;
409                 p->list = NULL;
410                 free(p);
411         }
412
413         context->plugin_list = NULL;
414
415         return 0;
416 }
417
418 #endif
419 #endif
420
421
422 #if 0
423 static void
424 sigabrt_handler(int x)
425 {
426         printf("%s\n", __func__);
427         //*(char *)0 = 0;
428 }
429 #endif
430
431 LWS_VISIBLE int
432 lws_plat_context_early_init(void)
433 {
434         signal(SIGPIPE, SIG_IGN);
435
436 //      signal(SIGABRT, sigabrt_handler);
437
438         return 0;
439 }
440
441 LWS_VISIBLE void
442 lws_plat_context_early_destroy(struct lws_context *context)
443 {
444 }
445
446 LWS_VISIBLE void
447 lws_plat_context_late_destroy(struct lws_context *context)
448 {
449         struct lws_context_per_thread *pt = &context->pt[0];
450         int m = context->count_threads;
451
452 #ifdef LWS_WITH_PLUGINS
453         if (context->plugin_list)
454                 lws_plat_plugins_destroy(context);
455 #endif
456
457         if (context->lws_lookup)
458                 lws_free(context->lws_lookup);
459
460         while (m--) {
461                 close(pt->dummy_pipe_fds[0]);
462                 close(pt->dummy_pipe_fds[1]);
463                 pt++;
464         }
465         close(context->fd_random);
466 }
467
468 /* cast a struct sockaddr_in6 * into addr for ipv6 */
469
470 LWS_VISIBLE int
471 lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr,
472                     size_t addrlen)
473 {
474         int rc = -1;
475
476         struct ifaddrs *ifr;
477         struct ifaddrs *ifc;
478 #ifdef LWS_USE_IPV6
479         struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr;
480 #endif
481
482         getifaddrs(&ifr);
483         for (ifc = ifr; ifc != NULL && rc; ifc = ifc->ifa_next) {
484                 if (!ifc->ifa_addr)
485                         continue;
486
487                 lwsl_info(" interface %s vs %s\n", ifc->ifa_name, ifname);
488
489                 if (strcmp(ifc->ifa_name, ifname))
490                         continue;
491
492                 switch (ifc->ifa_addr->sa_family) {
493                 case AF_INET:
494 #ifdef LWS_USE_IPV6
495                         if (ipv6) {
496                                 /* map IPv4 to IPv6 */
497                                 bzero((char *)&addr6->sin6_addr,
498                                                 sizeof(struct in6_addr));
499                                 addr6->sin6_addr.s6_addr[10] = 0xff;
500                                 addr6->sin6_addr.s6_addr[11] = 0xff;
501                                 memcpy(&addr6->sin6_addr.s6_addr[12],
502                                         &((struct sockaddr_in *)ifc->ifa_addr)->sin_addr,
503                                                         sizeof(struct in_addr));
504                         } else
505 #endif
506                                 memcpy(addr,
507                                         (struct sockaddr_in *)ifc->ifa_addr,
508                                                     sizeof(struct sockaddr_in));
509                         break;
510 #ifdef LWS_USE_IPV6
511                 case AF_INET6:
512                         memcpy(&addr6->sin6_addr,
513                           &((struct sockaddr_in6 *)ifc->ifa_addr)->sin6_addr,
514                                                        sizeof(struct in6_addr));
515                         break;
516 #endif
517                 default:
518                         continue;
519                 }
520                 rc = 0;
521         }
522
523         freeifaddrs(ifr);
524
525         if (rc == -1) {
526                 /* check if bind to IP adddress */
527 #ifdef LWS_USE_IPV6
528                 if (inet_pton(AF_INET6, ifname, &addr6->sin6_addr) == 1)
529                         rc = 0;
530                 else
531 #endif
532                 if (inet_pton(AF_INET, ifname, &addr->sin_addr) == 1)
533                         rc = 0;
534         }
535
536         return rc;
537 }
538
539 LWS_VISIBLE void
540 lws_plat_insert_socket_into_fds(struct lws_context *context, struct lws *wsi)
541 {
542         struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
543
544         lws_libev_io(wsi, LWS_EV_START | LWS_EV_READ);
545         lws_libuv_io(wsi, LWS_EV_START | LWS_EV_READ);
546
547         pt->fds[pt->fds_count++].revents = 0;
548 }
549
550 LWS_VISIBLE void
551 lws_plat_delete_socket_from_fds(struct lws_context *context,
552                                                 struct lws *wsi, int m)
553 {
554         struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
555
556         lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE);
557         lws_libuv_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE);
558
559         pt->fds_count--;
560 }
561
562 LWS_VISIBLE void
563 lws_plat_service_periodic(struct lws_context *context)
564 {
565         /* if our parent went down, don't linger around */
566         if (context->started_with_parent &&
567             kill(context->started_with_parent, 0) < 0)
568                 kill(getpid(), SIGTERM);
569 }
570
571 LWS_VISIBLE int
572 lws_plat_change_pollfd(struct lws_context *context,
573                       struct lws *wsi, struct lws_pollfd *pfd)
574 {
575         return 0;
576 }
577
578 LWS_VISIBLE const char *
579 lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt)
580 {
581         return inet_ntop(af, src, dst, cnt);
582 }
583
584 static lws_filefd_type
585 _lws_plat_file_open(struct lws *wsi, const char *filename,
586                     unsigned long *filelen, int flags)
587 {
588         struct stat stat_buf;
589         int ret = open(filename, flags, 0664);
590
591         if (ret < 0)
592                 return LWS_INVALID_FILE;
593
594         if (fstat(ret, &stat_buf) < 0) {
595                 close(ret);
596                 return LWS_INVALID_FILE;
597         }
598         *filelen = stat_buf.st_size;
599         return ret;
600 }
601
602 static int
603 _lws_plat_file_close(struct lws *wsi, lws_filefd_type fd)
604 {
605         return close(fd);
606 }
607
608 unsigned long
609 _lws_plat_file_seek_cur(struct lws *wsi, lws_filefd_type fd, long offset)
610 {
611         return lseek(fd, offset, SEEK_CUR);
612 }
613
614 static int
615 _lws_plat_file_read(struct lws *wsi, lws_filefd_type fd, unsigned long *amount,
616                     unsigned char *buf, unsigned long len)
617 {
618         long n;
619
620         n = read((int)fd, buf, len);
621         if (n == -1) {
622                 *amount = 0;
623                 return -1;
624         }
625
626         *amount = n;
627
628         return 0;
629 }
630
631 static int
632 _lws_plat_file_write(struct lws *wsi, lws_filefd_type fd, unsigned long *amount,
633                      unsigned char *buf, unsigned long len)
634 {
635         long n;
636
637         n = write((int)fd, buf, len);
638         if (n == -1) {
639                 *amount = 0;
640                 return -1;
641         }
642
643         *amount = n;
644
645         return 0;
646 }
647
648
649 LWS_VISIBLE int
650 lws_plat_init(struct lws_context *context,
651               struct lws_context_creation_info *info)
652 {
653         struct lws_context_per_thread *pt = &context->pt[0];
654         int n = context->count_threads, fd;
655
656         /* master context has the global fd lookup array */
657         context->lws_lookup = lws_zalloc(sizeof(struct lws *) *
658                                          context->max_fds);
659         if (context->lws_lookup == NULL) {
660                 lwsl_err("OOM on lws_lookup array for %d connections\n",
661                          context->max_fds);
662                 return 1;
663         }
664
665         lwsl_notice(" mem: platform fd map: %5u bytes\n",
666                     sizeof(struct lws *) * context->max_fds);
667         fd = open(SYSTEM_RANDOM_FILEPATH, O_RDONLY);
668
669         context->fd_random = fd;
670         if (context->fd_random < 0) {
671                 lwsl_err("Unable to open random device %s %d\n",
672                          SYSTEM_RANDOM_FILEPATH, context->fd_random);
673                 return 1;
674         }
675
676         if (!lws_libev_init_fd_table(context) &&
677             !lws_libuv_init_fd_table(context)) {
678                 /* otherwise libev handled it instead */
679
680                 while (n--) {
681                         if (pipe(pt->dummy_pipe_fds)) {
682                                 lwsl_err("Unable to create pipe\n");
683                                 return 1;
684                         }
685
686                         /* use the read end of pipe as first item */
687                         pt->fds[0].fd = pt->dummy_pipe_fds[0];
688                         pt->fds[0].events = LWS_POLLIN;
689                         pt->fds[0].revents = 0;
690                         pt->fds_count = 1;
691                         pt++;
692                 }
693         }
694
695         context->fops.open      = _lws_plat_file_open;
696         context->fops.close     = _lws_plat_file_close;
697         context->fops.seek_cur  = _lws_plat_file_seek_cur;
698         context->fops.read      = _lws_plat_file_read;
699         context->fops.write     = _lws_plat_file_write;
700
701 #ifdef LWS_WITH_PLUGINS
702         if (info->plugin_dirs)
703                 lws_plat_plugins_init(context, info->plugin_dirs);
704 #endif
705
706         return 0;
707 }