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