1 /*
2 * %CopyrightBegin%
3 *
4 * Copyright Ericsson AB 1998-2018. All Rights Reserved.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 * %CopyrightEnd%
19 */
20 /*
21 * Erlang port program to do the name service lookup for the erlang
22 * distribution and inet part of the kernel.
23 * A pool of subprocess is kept, to which a pair of pipes is connected.
24 * The main process schedules requests among the different subprocesses
25 * (created with fork()), to be able to handle as many requests as possible
26 * simultaneously. The controlling erlang machine may request a "cancel",
27 * in which case the process may be killed and restarted when the need arises.
28 * The single numeric parameter to this program is the maximum port pool size,
29 * which is the size of the bookkeeping array.
30 *
31 * Windows:
32 * There is instead of a pool of processes a pool of threads.
33 * Communication is not done through pipes but via message queues between
34 * the threads. The only "pipes" involved are the ones used for communicating
35 * with Erlang.
36 * Important note:
37 * For unknown reasons, the combination of a thread doing blocking I/O on
38 * a named pipe at the same time as another thread tries to resolve a hostname
39 * may (with certain software configurations) block the gethostbyname call (!)
40 * For that reason, standard input (and standard output) should be opened
41 * in asynchronous mode (FILE_FLAG_OVERLAPPED), which has to be done by Erlang.
42 * A special flag to open_port is used to work around this behaviour in winsock
43 * and the threads doing read and write handle asynchronous I/O.
44 * The ReadFile and WriteFile calls try to cope with both types of I/O, why
45 * the code is not really as Microsoft describes "the right way to do it" in
46 * their documentation. Important to note is that **there is no supported way
47 * to retrieve the information if the HANDLE was opened with
48 * FILE_FLAG_OVERLAPPED from the HANDLE itself**.
49 *
50 */
51
52 #ifdef HAVE_CONFIG_H
53 # include "config.h"
54 #endif
55
56 #include "erl_printf.h"
57
58 #ifdef WIN32
59
60 #define WIN32_LEAN_AND_MEAN
61 #include <winsock2.h>
62 #include <windows.h>
63 #include <ws2tcpip.h>
64 #include <process.h>
65 #include <stdio.h>
66 #include <stdlib.h>
67
68 /* These are not used even if they would exist which they should not */
69 #undef HAVE_GETIPNODEBYNAME
70 #undef HAVE_GETHOSTBYNAME2
71 #undef HAVE_GETIPNODEBYADDR
72
73 #else /* Unix */
74
75 #include <stdio.h>
76 #include <stdlib.h>
77 #include <string.h>
78 #include <unistd.h>
79 #include <stdarg.h>
80 #include <sys/types.h>
81 #include <sys/socket.h>
82 #include <sys/wait.h>
83 #include <netinet/in.h>
84 #include <arpa/inet.h>
85 #include <netdb.h>
86 #include <errno.h>
87 #include <signal.h>
88
89 #ifdef HAVE_SYS_TIME_H
90 #include <sys/time.h>
91 #else
92 #include <time.h>
93 #endif
94 #include <sys/times.h>
95
96 #ifndef RETSIGTYPE
97 #define RETSIGTYPE void
98 #endif
99
100 /* To simplify #ifdef code further down - select only one to be defined...
101 ** Use them in pairs - if one is broken do not trust its mate.
102 **/
103 #if defined(HAVE_GETADDRINFO) && defined(HAVE_GETNAMEINFO)
104 #undef HAVE_GETIPNODEBYNAME
105 #undef HAVE_GETIPNODEBYADDR
106 #undef HAVE_GETHOSTBYNAME2
107 #elif defined(HAVE_GETIPNODEBYNAME) && defined(HAVE_GETIPNODEBYADDR)
108 #undef HAVE_GETADDRINFO
109 #undef HAVE_GETNAMEINFO
110 #undef HAVE_GETHOSTBYNAME2
111 #else
112 #undef HAVE_GETIPNODEBYNAME
113 #undef HAVE_GETIPNODEBYADDR
114 #undef HAVE_GETADDRINFO
115 #undef HAVE_GETNAMEINFO
116 #endif
117
118 #endif /* !WIN32 */
119
120 #define PACKET_BYTES 4
121 #ifdef WIN32
122 #define READ_PACKET_BYTES(X,Y,Z) read_int32((X),(Y),(Z))
123 #else
124 #define READ_PACKET_BYTES(X,Y) read_int32((X),(Y))
125 #endif
126 #define PUT_PACKET_BYTES(X,Y) put_int32((X),(Y))
127 /* The serial numbers of the requests */
128 typedef int SerialType;
129
130 #define INVALID_SERIAL -1
131
132 /* The operations performed by this program */
133 typedef unsigned char OpType;
134
135 #define OP_GETHOSTBYNAME 1
136 #define OP_GETHOSTBYADDR 2
137 #define OP_CANCEL_REQUEST 3
138 #define OP_CONTROL 4
139
140 /* The protocol (IPV4/IPV6) */
141 typedef unsigned char ProtoType;
142
143 #define PROTO_IPV4 1
144 #define PROTO_IPV6 2
145
146 /* OP_CONTROL */
147 typedef unsigned char CtlType;
148 #define SETOPT_DEBUG_LEVEL 0
149
150 /* The unit of an IP address (0 == error, 4 == IPV4, 16 == IPV6) */
151 typedef unsigned char UnitType;
152
153 #define UNIT_ERROR 0
154 #define UNIT_IPV4 4
155 #define UNIT_IPV6 16
156
157 /* And the byte type */
158 typedef unsigned char AddrByte; /* Must be compatible with character
159 datatype */
160
161 /*
162 * Marshalled format of request:
163 *{
164 * Serial: 32 bit big endian
165 * Op:8 bit [1,2,3]
166 * If op == 1 {
167 * Proto:8 bit [1,2]
168 * Str: Null terminated array of characters
169 * } Else if op == 2 {
170 * Proto:8 bit [1,2]
171 * If proto == 1 {
172 * B0..B3: 4 bytes, most significant first
173 * } Else (proto == 2) {
174 * B0..B15: 16 bytes, most significant first
175 * }
176 * }
177 * (No more if op == 3)
178 *}
179 * The request arrives as a packet, with 4 packet size bytes.
180 */
181
182 /* The main process unpackes the marshalled message and sends the data
183 * to a suitable port process or, in the case of a close request, kills the
184 * suitable port process. There is also a que of requests linked together,
185 * for when all subrocesses are busy.
186 */
187
188 typedef struct QueItem {
189 struct QueItem *next;
190 int req_size;
191 AddrByte request[1];
192 } QueItem; /* Variable size due to request's variable size */
193
194 QueItem *que_first;
195 QueItem *que_last;
196
197 #ifdef WIN32
198 typedef struct mesq {
199 HANDLE data_present;
200 CRITICAL_SECTION crit;
201 int shutdown;
202 QueItem *first;
203 QueItem *last;
204 } MesQ;
205
206 MesQ *to_erlang;
207 MesQ *from_erlang;
208 #endif
209
210 /*
211 * Marshalled format of reply:
212 *{
213 * Serial: 32 bit big endian
214 * Unit: 8 bit, same as h_length or 0 for error
215 * if unit == 0 {
216 * Str: Null terminated character string explaining the error
217 * } else {
218 * Naddr: 32 bit big endian
219 * if unit = 4 {
220 * (B0..B3)0..(B0..B3)Naddr-1: Naddr*4 bytes most significant first
221 * } else if unit == 16 {
222 * (B0..B15)0..(B0..B15)Naddr-1: Naddr*16 bytes most significant first
223 * }
224 * Nnames: 32 bit big endian >= 1
225 * Name0: Null terminated string of characters
226 * Alias[0]..Alias[Nnames - 2]: Nnames - 1 Null terminated strings of chars
227 * }
228 *}
229 * Four packet size bytes prepended (big endian)
230 */
231 /* Internal error codes */
232 #define ERRCODE_NOTSUP 1
233 #define ERRCODE_HOST_NOT_FOUND 2
234 #define ERRCODE_TRY_AGAIN 3
235 #define ERRCODE_NO_RECOVERY 4
236 #define ERRCODE_NO_DATA 5
237 #define ERRCODE_NETDB_INTERNAL 7
238
239 /*
240 * Each worker process is represented in the parent by the following struct
241 */
242
243 typedef unsigned WorkerState;
244
245 #define WORKER_EMPTY 0 /* No process created */
246 #define WORKER_FREE 1 /* Living waiting process */
247 #define WORKER_BUSY 2 /* Living busy process */
248 #define WORKER_STALLED 3 /* Living cancelled process */
249
250 /* The timeout when killing a child process in seconds*/
251 #define CHILDWAIT_TMO 1
252 /* The domainname size_limit */
253 #define DOMAINNAME_MAX 258 /* 255 + Opcode + Protocol + Null termination */
254
255 typedef struct {
256 WorkerState state;
257 #ifdef WIN32
258 DWORD pid; /* 0 if unused */
259 MesQ *writeto; /* Message queues */
260 MesQ *readfrom;
261 #else
262 pid_t pid; /* -1 if unused */
263 int writeto, readfrom; /* Pipes */
264 #endif
265 SerialType serial;
266 AddrByte domain[DOMAINNAME_MAX];
267 QueItem *que_first;
268 QueItem *que_last;
269 int que_size;
270 } Worker;
271
272 int num_busy_workers;
273 int num_free_workers;
274 int num_stalled_workers;
275 int max_workers;
276 int greedy_threshold;
277 Worker *busy_workers; /* Workers doing any job that someone really is
278 interested in */
279 Worker *free_workers; /* Really free workers */
280 Worker *stalled_workers; /* May still deliver answers which we will
281 discard */
282 #define BEE_GREEDY() (num_busy_workers >= greedy_threshold)
283
284 static char *program_name;
285
286 static int debug_level;
287 #ifdef WIN32
288 static HANDLE debug_console_allocated = INVALID_HANDLE_VALUE;
289 #endif
290
291 #ifdef NODEBUG
292 #define DEBUGF(L,P) /* Nothing */
293 #else
294 #define DEBUGF(Level,Printf) do { if (debug_level >= (Level)) \
295 debugf Printf;} while(0)
296 #endif
297 #define ALLOC(Size) my_malloc(Size)
298 #define REALLOC(Old, Size) my_realloc((Old), (Size))
299 #define FREE(Ptr) free(Ptr)
300
301 #ifdef WIN32
302 #define WAKEUP_WINSOCK() do { \
303 char dummy_buff[100]; \
304 gethostname(dummy_buff,99); \
305 } while (0)
306 #endif
307
308 /* The internal prototypes */
309 static char *format_address(int siz, AddrByte *addr);
310 static void debugf(char *format, ...);
311 static void warning(char *format, ...);
312 static void fatal(char *format, ...);
313 static void *my_malloc(size_t size);
314 static void *my_realloc(void *old, size_t size);
315 static int get_int32(AddrByte *buff);
316 static void put_int32(AddrByte *buff, int value);
317 static int create_worker(Worker *pworker, int save_que);
318 static int map_netdb_error(int netdb_code);
319 #if defined(HAVE_GETADDRINFO) || defined(HAVE_GETNAMEINFO)
320 static int map_netdb_error_ai(int netdb_code);
321 #endif
322 static char *errcode_to_string(int errcode);
323 static size_t build_error_reply(SerialType serial, int errnum,
324 AddrByte **preply,
325 size_t *preply_size);
326 #ifdef HAVE_GETADDRINFO
327 static size_t build_reply_ai(SerialType serial, int, struct addrinfo *,
328 AddrByte **preply, size_t *preply_size);
329 #endif
330 static size_t build_reply(SerialType serial, struct hostent *he,
331 AddrByte **preply, size_t *preply_size);
332 static int read_request(AddrByte **buff, size_t *buff_size);
333 static OpType get_op(AddrByte *buff);
334 static AddrByte *get_op_addr(AddrByte *buff);
335 static SerialType get_serial(AddrByte *buff);
336 static ProtoType get_proto(AddrByte *buff);
337 static CtlType get_ctl(AddrByte *buff);
338 static AddrByte *get_data(AddrByte *buff);
339 static int get_debug_level(AddrByte *buff);
340 static int relay_reply(Worker *pw);
341 static int ignore_reply(Worker *pw);
342 static void init_workers(int max);
343 static void kill_worker(Worker *pw);
344 static Worker *pick_worker(void);
345 static void kill_last_picked_worker(void);
346 static void stall_worker(SerialType serial);
347 static int handle_io_busy(int ndx);
348 static int handle_io_free(int ndx);
349 static int handle_io_stalled(int ndx);
350 static void check_que(void);
351 static void main_loop(void);
352 static void usage(char *unknown);
353 static void domaincopy(AddrByte *out,AddrByte *in);
354 static int domaineq(AddrByte *d1, AddrByte *d2);
355 static int get_domainname(AddrByte *inbuff, int insize, AddrByte *domainbuff);
356 static Worker *pick_worker_greedy(AddrByte *domainbuff);
357 static void restart_worker(Worker *w);
358 static void start_que_request(Worker *w) ;
359 #ifdef WIN32
360 static int read_int32(HANDLE fd, int *res, HANDLE ev);
361 static int read_exact(HANDLE fd, void *vbuff, DWORD nbytes, HANDLE ev);
362 static int write_exact(HANDLE fd, AddrByte *buff, DWORD len,HANDLE ev);
363 DWORD WINAPI worker_loop(void *v);
364 DWORD WINAPI reader(void *data);
365 DWORD WINAPI writer(void *data);
366 static int send_mes_to_worker(QueItem *m, Worker *pw);
367 BOOL create_mesq(MesQ **q);
368 BOOL enque_mesq(MesQ *q, QueItem *m);
369 BOOL deque_mesq(MesQ *q, QueItem **m);
370 BOOL close_mesq(MesQ *q);
371 HANDLE event_mesq(MesQ *q);
372 #else
373 static size_t read_int32(int fd, int *res);
374 static ssize_t read_exact(int fd, void *vbuff, size_t nbytes);
375 static int write_exact(int fd, AddrByte *buff, int len);
376 void reap_children(int ignored);
377 static void init_signals(void);
378 static void kill_all_workers(void);
379 static void close_all_worker_fds(void);
380 static int worker_loop(void);
381 static int fillin_reply(Worker *pw);
382 static int send_request_to_worker(AddrByte *pr, int rsize, Worker *pw);
383 #endif
384
385 #define ERL_DBG_LVL_ENV_VAR "ERL_INET_GETHOST_DEBUG"
386
387 static int
get_env_debug_level(void)388 get_env_debug_level(void)
389 {
390 #ifdef __WIN32__
391 char value[21]; /* Enough for any 64-bit values */
392 DWORD sz = GetEnvironmentVariable((LPCTSTR) ERL_DBG_LVL_ENV_VAR,
393 (LPTSTR) value,
394 (DWORD) sizeof(value));
395 if (sz == 0 || sz > sizeof(value))
396 return 0;
397 #else
398 char *value = getenv(ERL_DBG_LVL_ENV_VAR);
399 if (!value)
400 return 0;
401 #endif
402 return atoi(value);
403 }
404
405 #ifdef WIN32
do_allocate_console(void)406 static void do_allocate_console(void)
407 {
408 AllocConsole();
409 debug_console_allocated = CreateFile ("CONOUT$", GENERIC_WRITE,
410 FILE_SHARE_WRITE, NULL,
411 OPEN_EXISTING,
412 FILE_ATTRIBUTE_NORMAL, NULL);
413 }
414 #ifdef HARDDEBUG
415 DWORD WINAPI pseudo_worker_loop(void *v);
416 static void poll_gethost(int row);
417 #endif
418 #endif
419
420 /*
421 * Main
422 */
main(int argc,char ** argv)423 int main(int argc, char **argv)
424 {
425 int num_workers = 1;
426 char **ap = argv + 1;
427 int x;
428 int disable_greedy = 0;
429
430 program_name = *argv;
431 que_first = que_last = NULL;
432 debug_level = get_env_debug_level();
433 greedy_threshold = 0;
434
435 while (*ap) {
436 if (!strcmp(*ap, "-d")) {
437 ++debug_level;
438 } else if(!strcmp(*ap, "-g") && *(ap + 1)) {
439 ++ap;
440 x = atoi(*ap);
441 if (!x) {
442 usage(*ap);
443 } else {
444 greedy_threshold = x;
445 }
446 } else if(!strcmp(*ap, "-ng")) {
447 disable_greedy = 1;
448 } else {
449 x = atoi(*ap);
450 if (!x) {
451 usage(*ap);
452 } else {
453 num_workers = x;
454 }
455 }
456 ++ap;
457 }
458
459 #ifdef WIN32
460 if (num_workers > 60 || greedy_threshold > 60) {
461 usage("More than 60 workers on windows impossible!");
462 num_workers = 60;
463 greedy_threshold = 0;
464 }
465 #endif
466
467 if(!greedy_threshold) {
468 greedy_threshold = (3*num_workers)/4; /* 75% */
469 if (!greedy_threshold) {
470 greedy_threshold = num_workers;
471 }
472 }
473
474 if (disable_greedy) {
475 greedy_threshold = num_workers + 1;
476 }
477
478 #ifdef WIN32
479 {
480 WORD wr;
481 WSADATA wsa_data;
482 int wsa_error;
483 wr = MAKEWORD(2,0);
484
485 wsa_error = WSAStartup(wr,&wsa_data);
486 if (wsa_error) {
487 fatal("Could not open usable winsock library.");
488 }
489 if (LOBYTE(wsa_data.wVersion) != 2 || HIBYTE(wsa_data.wVersion) != 0) {
490 fatal("Could not open recent enough winsock library.");
491 }
492
493 if (debug_level >= 1) {
494 do_allocate_console();
495
496 DEBUGF(1,("num_workers = %d, greedy_threshold = %d, "
497 "debug_level = %d.",
498 num_workers, greedy_threshold, debug_level));
499 }
500 }
501 WAKEUP_WINSOCK(); /* Why on earth is this needed? */
502
503 #endif
504
505 init_workers(num_workers);
506
507 main_loop();
508 #ifndef WIN32
509 kill_all_workers();
510 #endif
511 return 0;
512 }
513
usage(char * unknown)514 static void usage(char *unknown)
515 {
516 fprintf(stderr,"%s: Unknown option \"%s\"\n"
517 "Usage: %s [-d [-d ...]] [-g <greedy threshold>] "
518 "[<number of workers>]\n",
519 program_name, unknown, program_name);
520 }
521
522 /*
523 * Main process main loop
524 */
525
handle_io_busy(int ndx)526 static int handle_io_busy(int ndx)
527 {
528 /* Probably an answer */
529 int res;
530 res = relay_reply(&busy_workers[ndx]);
531 if (res < 0) {
532 /* Bad worker */
533 if (busy_workers[ndx].que_size) {
534 restart_worker(&busy_workers[ndx]);
535 start_que_request(&busy_workers[ndx]);
536 return 0;
537 } else {
538 kill_worker(&busy_workers[ndx]);
539 --num_busy_workers;
540 busy_workers[ndx] = busy_workers[num_busy_workers];
541 }
542 return 1;
543 } else if (res == 0) {
544 /* Erlang has closed */
545 return -1;
546 } else {
547 if (busy_workers[ndx].que_size) {
548 start_que_request(&busy_workers[ndx]);
549 return 0;
550 }
551 /* The worker is no longer busy, it should be in the free list */
552 free_workers[num_free_workers] = busy_workers[ndx];
553 free_workers[num_free_workers].state = WORKER_FREE;
554 ++num_free_workers;
555 --num_busy_workers;
556 busy_workers[ndx] = busy_workers[num_busy_workers];
557 return 1;
558 }
559 }
560
handle_io_free(int ndx)561 static int handle_io_free(int ndx)
562 {
563 /* IO from a free worker means "kill me" */
564 DEBUGF(1,("Free worker[%ld] spontaneously died.",
565 (long) free_workers[ndx].pid));
566 kill_worker(&free_workers[ndx]);
567 --num_free_workers;
568 free_workers[ndx] = free_workers[num_free_workers];
569 return 1;
570 }
571
handle_io_stalled(int ndx)572 static int handle_io_stalled(int ndx)
573 {
574 int res;
575 res = ignore_reply(&stalled_workers[ndx]);
576 if (res <= 0) {
577 /* Bad worker */
578 kill_worker(&stalled_workers[ndx]);
579 --num_stalled_workers;
580 stalled_workers[ndx] = stalled_workers[num_stalled_workers];
581 return 1;
582 } else {
583 DEBUGF(3,("Ignoring reply from stalled worker[%ld].",
584 (long) stalled_workers[ndx].pid));
585 free_workers[num_free_workers] = stalled_workers[ndx];
586 free_workers[num_free_workers].state = WORKER_FREE;
587 ++num_free_workers;
588 --num_stalled_workers;
589 stalled_workers[ndx] = stalled_workers[num_stalled_workers];
590 return 1;
591 }
592 }
593
check_que(void)594 static void check_que(void)
595 {
596 /* Check if anything in the que can be handled */
597 Worker *cw;
598
599 while (que_first) {
600 QueItem *qi,*nxt;
601 qi = que_first;
602 nxt = qi->next; /* Need to save before it's getting put in another que
603 in threaded solution */
604 if ((cw = pick_worker()) == NULL) {
605 break;
606 }
607 #ifdef WIN32
608 {
609 SerialType save_serial = get_serial(que_first->request);
610 if (send_mes_to_worker(que_first, cw) != 0) {
611 kill_last_picked_worker();
612 continue;
613 }
614 cw->serial = save_serial;
615 }
616 #else
617 if (send_request_to_worker(que_first->request,
618 que_first->req_size, cw) != 0) {
619 /* Couldn't send request, kill the worker and retry */
620 kill_last_picked_worker();
621 continue;
622 }
623 cw->serial = get_serial(que_first->request);
624 #endif
625 /* Went well, lets deque */
626 que_first = nxt;
627 if (que_first == NULL) {
628 que_last = NULL;
629 }
630 DEBUGF(3,("Did deque serial %d, Que is %sempty",
631 get_serial(qi->request), (que_first) ? "not " : ""));
632 #ifndef WIN32
633 FREE(qi);
634 #endif
635 }
636 }
637
clean_que_of(SerialType s)638 static int clean_que_of(SerialType s)
639 {
640 QueItem **qi;
641 int i;
642
643 for(qi=&que_first;*qi != NULL &&
644 s != get_serial((*qi)->request); qi = &((*qi)->next))
645 ;
646 if(*qi != NULL) {
647 QueItem *r = *qi;
648 *qi = (*qi)->next;
649 FREE(r);
650 if(que_last == r) {
651 /* Lost the "last" pointer, should be very uncommon
652 if the que is not empty, so we simply do a traversal
653 to reclaim it. */
654 if (que_first == NULL) {
655 que_last = NULL;
656 } else {
657 for (que_last=que_first;que_last->next != NULL;
658 que_last = que_last->next)
659 ;
660 }
661 }
662 DEBUGF(3,("Removing serial %d from global que on request, "
663 "que %sempty",s, (que_first) ? "not " : ""));
664 return 1;
665 }
666 for (i = 0; i < num_busy_workers; ++i) {
667 for(qi=&(busy_workers[i].que_first);*qi != NULL &&
668 s != get_serial((*qi)->request); qi = &((*qi)->next))
669 ;
670 if(*qi != NULL) {
671 QueItem *r = *qi;
672 *qi = (*qi)->next;
673 FREE(r);
674 if(busy_workers[i].que_last == r) {
675 /* Lost the "last" pointer, should be very uncommon
676 if the que is not empty, so we simply do a traversal
677 to reclaim it. */
678 if (busy_workers[i].que_first == NULL) {
679 busy_workers[i].que_last = NULL;
680 if (busy_workers[i].que_size != 1) {
681 fatal("Worker que size counter incorrect, internal datastructure error.");
682 }
683 } else {
684 for (busy_workers[i].que_last = busy_workers[i].que_first;
685 busy_workers[i].que_last->next != NULL;
686 busy_workers[i].que_last = busy_workers[i].que_last->next)
687 ;
688 }
689 }
690 --(busy_workers[i].que_size);
691 DEBUGF(3,("Removing serial %d from worker[%ld] specific que "
692 "on request, que %sempty",
693 s, (long) busy_workers[i].pid,
694 (busy_workers[i].que_first) ? "not " : ""));
695 return 1;
696 }
697 }
698 return 0;
699 }
700
main_loop(void)701 static void main_loop(void)
702 {
703 AddrByte *inbuff = NULL;
704 int insize;
705 int i,w;
706 #ifdef WIN32
707 HANDLE handles[64];
708 DWORD num_handles;
709 DWORD index;
710 QueItem *qi;
711 #else
712 size_t inbuff_size = 0;
713 fd_set fds;
714 int max_fd;
715 #endif
716 int new_data;
717 int save_serial;
718 /* It's important that the free workers list is handled first */
719 Worker *workers[3] = {free_workers, busy_workers, stalled_workers};
720 int *wsizes[3] = {&num_free_workers, &num_busy_workers,
721 &num_stalled_workers};
722 int (*handlers[3])(int) = {&handle_io_free, &handle_io_busy,
723 &handle_io_stalled};
724 Worker *cw;
725 AddrByte domainbuff[DOMAINNAME_MAX];
726
727 #ifdef WIN32
728
729 {
730 DWORD dummy;
731 /* Create the reader and writer */
732 if ((!create_mesq(&to_erlang)) || (!create_mesq(&from_erlang))) {
733 fatal("Could not create message que! errno = %d.",GetLastError());
734 }
735 if (((HANDLE) _beginthreadex(NULL,0,writer,to_erlang,0,&dummy))
736 == NULL) {
737 fatal("Could not create writer thread! errno = %d.",GetLastError());
738 }
739 if (((HANDLE) _beginthreadex(NULL,0,reader,from_erlang,0,&dummy))
740 == NULL) {
741 fatal("Could not create reader thread! errno = %d.",GetLastError());
742 }
743 DEBUGF(4,("Created reader and writer threads."));
744 #ifdef HARDDEBUG
745 poll_gethost(__LINE__);
746 #endif
747 }
748 #endif
749
750 for(;;) {
751 #ifdef WIN32
752 num_handles = 0;
753 handles[num_handles++] = event_mesq(from_erlang);
754 for (w = 0; w < 3; ++w) {
755 for (i = 0; i < *wsizes[w]; ++i) {
756 handles[num_handles++] = event_mesq(workers[w][i].readfrom);
757 }
758 }
759
760 if ((index = WaitForMultipleObjects(num_handles, handles, FALSE, INFINITE))
761 == WAIT_FAILED) {
762 fatal("Could not WaitForMultpleObjects! errno = %d.",GetLastError());
763 }
764 w = 0;
765 index -= WAIT_OBJECT_0;
766
767 DEBUGF(4,("Got data on index %d.",index));
768 if (index > 0) {
769 if (((int)index - 1) < *wsizes[0]) {
770 (*handlers[0])(index - 1);
771 } else if (((int)index - 1) < ((*wsizes[0]) + (*wsizes[1]))) {
772 (*handlers[1])(index - 1 - (*wsizes[0]));
773 } else {
774 (*handlers[2])(index - 1 - (*wsizes[0]) - (*wsizes[1]));
775 }
776 }
777 new_data = (index == 0);
778 #else
779 max_fd = 0;
780 FD_ZERO(&fds);
781 FD_SET(0,&fds);
782 for (w = 0; w < 3; ++w) {
783 for (i = 0; i < *wsizes[w]; ++i) {
784 FD_SET(workers[w][i].readfrom,&fds);
785 if (workers[w][i].readfrom > max_fd) {
786 max_fd = workers[w][i].readfrom;
787 }
788 }
789 }
790 for (;;) {
791 if (select(max_fd + 1,&fds,NULL,NULL,NULL) < 0) {
792 if (errno == EINTR) {
793 continue;
794 } else {
795 fatal("Select failed (invalid internal structures?), "
796 "errno = %d.",errno);
797 }
798 }
799 break;
800 }
801 for (w = 0; w < 3; ++w) {
802 for (i = 0; i < *wsizes[w]; ++i) {
803 if (FD_ISSET(workers[w][i].readfrom, &fds)) {
804 int hres = (*handlers[w])(i);
805 if (hres < 0) {
806 return;
807 } else {
808 i -= hres; /* We'll retry this position, if hres == 1.
809 The position is usually
810 replaced with another worker,
811 a worker with
812 I/O usually changes state as we
813 use blocking file I/O */
814 }
815 }
816 }
817 }
818 new_data = FD_ISSET(0,&fds);
819
820 #endif
821
822 check_que();
823
824 /* Now check for new requests... */
825 if (new_data) { /* Erlang... */
826 OpType op;
827 #ifdef WIN32
828 if (!deque_mesq(from_erlang,&qi)) {
829 DEBUGF(1,("Erlang has closed."));
830 return;
831 }
832 insize = qi->req_size;
833 inbuff = qi->request;
834 DEBUGF(4,("Got data from erlang."));
835 DEBUGF(4,("OPeration == %d.",get_op(inbuff)));
836 #else
837 insize = read_request(&inbuff, &inbuff_size);
838 if (insize == 0) { /* Other errors taken care of in
839 read_request */
840 DEBUGF(1,("Erlang has closed."));
841 return;
842 }
843 #endif
844 op = get_op(inbuff);
845 if (op == OP_CANCEL_REQUEST) {
846 SerialType serial = get_serial(inbuff);
847 if (!clean_que_of(serial)) {
848 for (i = 0; i < num_busy_workers; ++i) {
849 if (busy_workers[i].serial == serial) {
850 if (busy_workers[i].que_size) {
851 restart_worker(&busy_workers[i]);
852 start_que_request(&busy_workers[i]);
853 } else {
854 stall_worker(i);
855 check_que();
856 }
857 break;
858 }
859 }
860 }
861 #ifdef WIN32
862 FREE(qi);
863 #endif
864 continue; /* New select */
865 } else if (op == OP_CONTROL) {
866 CtlType ctl;
867 SerialType serial = get_serial(inbuff);
868 if (serial != INVALID_SERIAL) {
869 fatal("Invalid serial: %d.", serial);
870 }
871 switch (ctl = get_ctl(inbuff)) {
872 case SETOPT_DEBUG_LEVEL:
873 {
874 int tmp_debug_level = get_debug_level(inbuff);
875 #ifdef WIN32
876 if (debug_console_allocated == INVALID_HANDLE_VALUE &&
877 tmp_debug_level > 0) {
878 DWORD res;
879 do_allocate_console();
880 WriteFile(debug_console_allocated,
881 "Hej\n",4,&res,NULL);
882 }
883 #endif
884 debug_level = tmp_debug_level;
885 DEBUGF(debug_level, ("debug_level = %d", debug_level));
886 for (w = 0; w < 3; ++w) {
887 for (i = 0; i < *wsizes[w]; i++) {
888 int res;
889 #ifdef WIN32
890 QueItem *m;
891 #endif
892 cw = &(workers[w][i]);
893 #ifdef WIN32
894 m = ALLOC(sizeof(QueItem) - 1 + qi->req_size);
895 memcpy(m->request, qi->request,
896 (m->req_size = qi->req_size));
897 m->next = NULL;
898 if ((res = send_mes_to_worker(m, cw)) != 0) {
899 FREE(m);
900 }
901 #else
902 res = send_request_to_worker(inbuff, insize, cw);
903 #endif
904 if (res != 0) {
905 kill_worker(cw);
906 (*wsizes[w])--;
907 *cw = workers[w][*wsizes[w]];
908 }
909 }
910 }
911 }
912 break;
913 default:
914 warning("Unknown control requested from erlang (%d), "
915 "message discarded.", (int) ctl);
916 break;
917 }
918 #ifdef WIN32
919 FREE(qi);
920 #endif
921 continue; /* New select */
922 } else {
923 ProtoType proto;
924 if (op != OP_GETHOSTBYNAME && op != OP_GETHOSTBYADDR) {
925 warning("Unknown operation requested from erlang (%d), "
926 "message discarded.", op);
927 #ifdef WIN32
928 FREE(qi);
929 #endif
930 continue;
931 }
932 if ((proto = get_proto(inbuff)) != PROTO_IPV4 &&
933 proto != PROTO_IPV6) {
934 warning("Unknown protocol requested from erlang (%d), "
935 "message discarded.", proto);
936 #ifdef WIN32
937 FREE(qi);
938 #endif
939 continue;
940 }
941 if (get_domainname(inbuff,insize,domainbuff) < 0) {
942 warning("Malformed message sent from erlang, no domain, "
943 "message discarded.", op);
944 #ifdef WIN32
945 FREE(qi);
946 #endif
947 continue;
948 }
949 }
950
951 if (BEE_GREEDY()) {
952 DEBUGF(4,("Beeing greedy!"));
953 if ((cw = pick_worker_greedy(domainbuff)) != NULL) {
954 /* Put it in the worker specific que if the
955 domainname matches... */
956 #ifndef WIN32
957 QueItem *qi = ALLOC(sizeof(QueItem) - 1 +
958 insize);
959 qi->req_size = insize;
960 memcpy(&(qi->request), inbuff, insize);
961 qi->next = NULL;
962 #endif
963 if (!cw->que_first) {
964 cw->que_first = cw->que_last = qi;
965 } else {
966 cw->que_last->next = qi;
967 cw->que_last = qi;
968 }
969 ++(cw->que_size);
970 continue;
971 }
972 /* Otherwise busyness as usual */
973 }
974
975 save_serial = get_serial(inbuff);
976
977 while ((cw = pick_worker()) != NULL) {
978 int res;
979 #ifdef WIN32
980 res = send_mes_to_worker(qi,cw);
981 #else
982 res = send_request_to_worker(inbuff, insize, cw);
983 #endif
984 if (res == 0) {
985 break;
986 } else {
987 kill_last_picked_worker();
988 }
989 }
990
991 if (cw == NULL) {
992 /* Insert into que */
993 #ifndef WIN32
994 QueItem *qi = ALLOC(sizeof(QueItem) - 1 +
995 insize);
996 qi->req_size = insize;
997 memcpy(&(qi->request), inbuff, insize);
998 qi->next = NULL;
999 #endif
1000 if (!que_first) {
1001 que_first = que_last = qi;
1002 } else {
1003 que_last->next = qi;
1004 que_last = qi;
1005 }
1006 } else {
1007 cw->serial = save_serial;
1008 domaincopy(cw->domain, domainbuff);
1009 }
1010 }
1011 }
1012 }
1013
1014 /*
1015 * Main process worker administration
1016 */
1017
init_workers(int max)1018 static void init_workers(int max)
1019 {
1020 max_workers = max;
1021 num_busy_workers = 0;
1022 num_free_workers = 0;
1023 num_stalled_workers = 0;
1024
1025 busy_workers = ALLOC(sizeof(Worker) * max_workers);
1026 free_workers = ALLOC(sizeof(Worker) * max_workers);
1027 stalled_workers = ALLOC(sizeof(Worker) * max_workers);
1028 #ifndef WIN32
1029 init_signals();
1030 #endif
1031 }
1032
1033 #ifdef WIN32
kill_worker(Worker * pw)1034 static void kill_worker(Worker *pw)
1035 {
1036 /* Cannot really kill a thread in win32, have to just leave it to die */
1037 close_mesq(pw->writeto);
1038 close_mesq(pw->readfrom);
1039 pw->state = WORKER_EMPTY;
1040 }
1041 #else
kill_worker(Worker * pw)1042 static void kill_worker(Worker *pw)
1043 {
1044 fd_set fds;
1045 struct timeval tmo;
1046 int selret;
1047 static char buff[1024];
1048
1049 DEBUGF(3,("Killing worker[%ld] with fd %d, serial %d",
1050 (long) pw->pid,
1051 (int) pw->readfrom,
1052 (int) pw->serial));
1053 kill(pw->pid, SIGUSR1);
1054 /* This is all just to check that the child died, not
1055 really necessary */
1056 for(;;) {
1057 FD_ZERO(&fds);
1058 FD_SET(pw->readfrom, &fds);
1059 tmo.tv_usec=0;
1060 tmo.tv_sec = CHILDWAIT_TMO;
1061 selret = select(pw->readfrom+1, &fds, NULL, NULL, &tmo);
1062 if (selret < 0) {
1063 if (errno != EINTR) {
1064 warning("Unable to select on dying child file descriptor, "
1065 "errno = %d.",errno);
1066 break;
1067 }
1068 } else if (selret == 0) {
1069 warning("Timeout waiting for child process to die, "
1070 "ignoring child (pid = %d).", pw->pid);
1071 break;
1072 } else {
1073 int ret;
1074 if ((ret = read(pw->readfrom, buff, 1024)) < 0) {
1075 if (errno != EINTR) {
1076 warning("Child file descriptor not closed properly, "
1077 "errno = %d", errno);
1078 break;
1079 }
1080 } else if (ret == 0) {
1081 break;
1082 }
1083 /* continue */
1084 }
1085 }
1086 /* Waiting is done by signal handler... */
1087 close(pw->readfrom);
1088 close(pw->writeto);
1089 pw->state = WORKER_EMPTY;
1090 /* Leave rest as is... */
1091 }
1092
kill_all_workers(void)1093 static void kill_all_workers(void)
1094 /* Emergency function, will not check that the children died... */
1095 {
1096 int i;
1097 for (i = 0; i < num_busy_workers; ++i) {
1098 kill(busy_workers[i].pid, SIGUSR1);
1099 }
1100 for (i = 0; i < num_free_workers; ++i) {
1101 kill(free_workers[i].pid, SIGUSR1);
1102 }
1103 for (i = 0; i < num_stalled_workers; ++i) {
1104 kill(stalled_workers[i].pid, SIGUSR1);
1105 }
1106 }
1107 #endif /* !WIN32 */
1108
pick_worker(void)1109 static Worker *pick_worker(void)
1110 {
1111 Worker tmp;
1112 if (num_free_workers > 0) {
1113 --num_free_workers;
1114 tmp = free_workers[num_free_workers];
1115 } else if (num_stalled_workers > 0) {
1116 /* "restart" the worker... */
1117 --num_stalled_workers;
1118 kill_worker(&(stalled_workers[num_stalled_workers]));
1119 if (create_worker(&tmp,0) < 0) {
1120 warning("Unable to create worker process, insufficient "
1121 "resources");
1122 return NULL;
1123 }
1124 } else {
1125 if (num_busy_workers == max_workers) {
1126 return NULL;
1127 }
1128 if (create_worker(&tmp,0) < 0) {
1129 warning("Unable to create worker process, insufficient "
1130 "resources");
1131 return NULL;
1132 }
1133 }
1134 /* tmp contains a worker now, make it busy and put it in the right
1135 array */
1136 tmp.state = WORKER_BUSY;
1137 busy_workers[num_busy_workers] = tmp;
1138 ++num_busy_workers;
1139 return &(busy_workers[num_busy_workers-1]);
1140 }
1141
pick_worker_greedy(AddrByte * domainbuff)1142 static Worker *pick_worker_greedy(AddrByte *domainbuff)
1143 {
1144 int i;
1145 int found = -1;
1146 for (i=0; i < num_busy_workers; ++i) {
1147 if (domaineq(busy_workers[i].domain, domainbuff)) {
1148 if ((found < 0) || (busy_workers[i].que_size <
1149 busy_workers[found].que_size)) {
1150 found = i;
1151 }
1152 }
1153 }
1154 if (found >= 0) {
1155 return &busy_workers[found];
1156 }
1157 return NULL;
1158 }
1159
restart_worker(Worker * w)1160 static void restart_worker(Worker *w)
1161 {
1162 kill_worker(w);
1163 if (create_worker(w,1) < 0) {
1164 fatal("Unable to create worker process, insufficient resources");
1165 }
1166 }
1167
kill_last_picked_worker(void)1168 static void kill_last_picked_worker(void)
1169 {
1170 kill_worker( &(busy_workers[num_busy_workers-1]));
1171 --num_busy_workers;
1172 }
1173
1174 /*
1175 * Starts a request qued to a specific worker, check_que starts normally queued requests.
1176 * We expect a que here...
1177 */
start_que_request(Worker * w)1178 static void start_que_request(Worker *w)
1179 {
1180 QueItem *qi;
1181 SerialType save_serial;
1182 if (!w->que_first || !w->que_size) {
1183 fatal("Expected que'd requests but found none, "
1184 "internal datastructure corrupted!");
1185 }
1186 qi = w->que_first;
1187 w->que_first = w->que_first->next;
1188 if (!w->que_first) {
1189 w->que_last = NULL;
1190 }
1191 --(w->que_size);
1192 save_serial = get_serial(qi->request);
1193 #ifdef WIN32
1194 while (send_mes_to_worker(qi, w) != 0) {
1195 restart_worker(w);
1196 }
1197 #else
1198 while (send_request_to_worker(qi->request,
1199 qi->req_size, w) != 0) {
1200 restart_worker(w);
1201 }
1202 #endif
1203 w->serial = save_serial;
1204 DEBUGF(3,("Did deque serial %d from worker[%ld] specific que, "
1205 "Que is %sempty",
1206 get_serial(qi->request), (long) w->pid,
1207 (w->que_first) ? "not " : ""));
1208 #ifndef WIN32
1209 FREE(qi);
1210 #endif
1211 }
1212
1213 #ifndef WIN32
1214 /* Signal utilities */
sys_sigset(int sig,RETSIGTYPE (* func)(int))1215 static RETSIGTYPE (*sys_sigset(int sig, RETSIGTYPE (*func)(int)))(int)
1216 {
1217 struct sigaction act, oact;
1218
1219 sigemptyset(&act.sa_mask);
1220 act.sa_flags = 0;
1221 act.sa_handler = func;
1222 sigaction(sig, &act, &oact);
1223 return(oact.sa_handler);
1224 }
1225
1226
sys_sigblock(int sig)1227 static void sys_sigblock(int sig)
1228 {
1229 sigset_t mask;
1230
1231 sigemptyset(&mask);
1232 sigaddset(&mask, sig);
1233 sigprocmask(SIG_BLOCK, &mask, (sigset_t *)NULL);
1234 }
1235
sys_sigrelease(int sig)1236 static void sys_sigrelease(int sig)
1237 {
1238 sigset_t mask;
1239
1240 sigemptyset(&mask);
1241 sigaddset(&mask, sig);
1242 sigprocmask(SIG_UNBLOCK, &mask, (sigset_t *)NULL);
1243 }
1244
1245 /* Child signal handler */
reap_children(int ignored)1246 void reap_children(int ignored)
1247 {
1248 int res;
1249 sys_sigblock(SIGCHLD);
1250 for (;;) {
1251 while ((res = waitpid((pid_t)-1, NULL, WNOHANG)) > 0)
1252 ;
1253 if (!(res < 0 && errno == EAGAIN)) {
1254 DEBUGF(4,("reap_children: res = %d, errno = %d.",res,errno));
1255 break;
1256 }
1257 }
1258 sys_sigrelease(SIGCHLD);
1259 }
1260
init_signals(void)1261 static void init_signals(void)
1262 {
1263 sys_sigset(SIGCHLD,&reap_children); /* SIG_IGN would give same result
1264 on most (?) platforms. */
1265 sys_sigset(SIGPIPE, SIG_IGN);
1266 }
1267 #endif
1268
stall_worker(int ndx)1269 static void stall_worker(int ndx)
1270 {
1271 --num_busy_workers;
1272 stalled_workers[num_stalled_workers] = busy_workers[ndx];
1273 stalled_workers[num_stalled_workers].state = WORKER_STALLED;
1274 busy_workers[ndx] = busy_workers[num_busy_workers];
1275 DEBUGF(3, ("Stalled worker[%ld]",
1276 (long) stalled_workers[num_stalled_workers].pid));
1277 ++num_stalled_workers;
1278 }
1279
1280
1281 /*
1282 * Main loop message passing
1283 */
1284 #ifndef WIN32
read_request(AddrByte ** buff,size_t * buff_size)1285 static int read_request(AddrByte **buff, size_t *buff_size)
1286 {
1287 int siz;
1288 int r;
1289
1290 if ((r = READ_PACKET_BYTES(0,&siz)) != PACKET_BYTES) {
1291 if (r == 0) {
1292 return 0;
1293 } else {
1294 fatal("Unexpected end of file on main input, errno = %d",errno);
1295 }
1296 }
1297
1298 if (siz > *buff_size) {
1299 if (*buff_size == 0) {
1300 *buff = ALLOC((*buff_size = siz));
1301 } else {
1302 *buff = REALLOC(*buff, (*buff_size = siz));
1303 }
1304 }
1305 if (read_exact(0,*buff, siz) != siz) {
1306 fatal("Unexpected end of file on main input, errno = %d",errno);
1307 }
1308 if (siz < 5) {
1309 fatal("Unexpected message on main input, message size %d less "
1310 "than minimum.");
1311 }
1312 return siz;
1313 }
1314
1315 #endif /* !WIN32 */
1316
get_op(AddrByte * buff)1317 static OpType get_op(AddrByte *buff)
1318 {
1319 return (OpType) buff[4];
1320 }
1321
get_op_addr(AddrByte * buff)1322 static AddrByte *get_op_addr(AddrByte *buff)
1323 {
1324 return buff + 4;
1325 }
1326
get_serial(AddrByte * buff)1327 static SerialType get_serial(AddrByte *buff)
1328 {
1329 return get_int32(buff);
1330 }
1331
get_proto(AddrByte * buff)1332 static ProtoType get_proto(AddrByte *buff)
1333 {
1334 return (ProtoType) buff[5];
1335 }
1336
get_ctl(AddrByte * buff)1337 static CtlType get_ctl(AddrByte *buff)
1338 {
1339 return (CtlType) buff[5];
1340 }
1341
get_data(AddrByte * buff)1342 static AddrByte *get_data(AddrByte *buff)
1343 {
1344 return buff + 6;
1345 }
1346
get_debug_level(AddrByte * buff)1347 static int get_debug_level(AddrByte *buff)
1348 {
1349 return get_int32(buff + 6);
1350 }
1351
1352 #ifdef WIN32
send_mes_to_worker(QueItem * m,Worker * pw)1353 static int send_mes_to_worker(QueItem *m, Worker *pw)
1354 {
1355 if (!enque_mesq(pw->writeto, m)) {
1356 warning("Unable to send to child process.");
1357 return -1;
1358 }
1359 return 0;
1360 }
1361 #else
send_request_to_worker(AddrByte * pr,int rsize,Worker * pw)1362 static int send_request_to_worker(AddrByte *pr, int rsize, Worker *pw)
1363 {
1364 AddrByte hdr[PACKET_BYTES];
1365
1366 PUT_PACKET_BYTES(hdr, rsize);
1367 if (write_exact(pw->writeto, hdr, PACKET_BYTES) < 0) {
1368 warning("Unable to write to child process.");
1369 return -1;
1370 }
1371 if (write_exact(pw->writeto, (AddrByte *) pr, rsize) < 0) {
1372 warning("Unable to write to child process.");
1373 return -1;
1374 }
1375 return 0;
1376 }
1377 #endif /* !WIN32 */
1378
1379 #ifdef WIN32
relay_reply(Worker * pw)1380 static int relay_reply(Worker *pw)
1381 {
1382 QueItem *m;
1383 if (!deque_mesq(pw->readfrom,&m)) {
1384 return 0;
1385 }
1386 if (!enque_mesq(to_erlang,m)) {
1387 FREE(m);
1388 return 0;
1389 }
1390 return 1;
1391 }
1392
ignore_reply(Worker * pw)1393 static int ignore_reply(Worker *pw) {
1394 QueItem *m;
1395 if (!deque_mesq(pw->readfrom,&m)) {
1396 return 0;
1397 }
1398 FREE(m);
1399 return 1;
1400 }
1401
1402 #else
1403
1404 /* Static buffers used by the next three functions */
1405 static AddrByte *relay_buff = NULL;
1406 static int relay_buff_size = 0;
1407
fillin_reply(Worker * pw)1408 static int fillin_reply(Worker *pw)
1409 {
1410 int length;
1411
1412 if (READ_PACKET_BYTES(pw->readfrom, &length) != PACKET_BYTES) {
1413 warning("Malformed reply (header) from worker process %d.",
1414 pw->pid);
1415 return -1;
1416 }
1417
1418 if (relay_buff_size < (length + PACKET_BYTES)) {
1419 if (!relay_buff_size) {
1420 relay_buff =
1421 ALLOC((relay_buff_size = (length + PACKET_BYTES)));
1422 } else {
1423 relay_buff =
1424 REALLOC(relay_buff,
1425 (relay_buff_size = (length + PACKET_BYTES)));
1426 }
1427 }
1428 PUT_PACKET_BYTES(relay_buff, length);
1429 if (read_exact(pw->readfrom, relay_buff + PACKET_BYTES, length) !=
1430 length) {
1431 warning("Malformed reply (data) from worker process %d.", pw->pid);
1432 return -1;
1433 }
1434 return length;
1435 }
1436
relay_reply(Worker * pw)1437 static int relay_reply(Worker *pw)
1438 {
1439 int length = fillin_reply(pw); /* Filled into the "global" buffer */
1440 int res;
1441
1442 if (length < 0) {
1443 return -1;
1444 }
1445 if ((res = write_exact(1, relay_buff, length + PACKET_BYTES)) < 0) {
1446 fatal("Cannot write reply to erlang process, errno = %d.", errno);
1447 } else if (res == 0) {
1448 DEBUGF(1,("Erlang has closed write pipe."));
1449 return 0;
1450 }
1451 return length;
1452 }
1453
ignore_reply(Worker * pw)1454 static int ignore_reply(Worker *pw)
1455 {
1456 return fillin_reply(pw);
1457 }
1458
1459 #endif /* !WIN32 */
1460
1461 /*
1462 * Domain name "parsing" and worker specific queing
1463 */
domaincopy(AddrByte * out,AddrByte * in)1464 static void domaincopy(AddrByte *out, AddrByte *in)
1465 {
1466 AddrByte *ptr = out;
1467 *ptr++ = *in++;
1468 *ptr++ = *in++;
1469 switch(*out) {
1470 case OP_GETHOSTBYNAME:
1471 while(*in != '\0' && *in != '.')
1472 ++in;
1473 strncpy((char*)ptr, (char*)in, DOMAINNAME_MAX-2);
1474 ptr[DOMAINNAME_MAX-3] = '\0';
1475 DEBUGF(4,("Saved domainname %s.", ptr));
1476 return;
1477 case OP_GETHOSTBYADDR:
1478 memcpy(ptr,in, ((out[1] == PROTO_IPV4) ? UNIT_IPV4 : UNIT_IPV6) - 1);
1479 DEBUGF(4, ("Saved domain address: %s.",
1480 format_address(((out[1] == PROTO_IPV4) ?
1481 UNIT_IPV4 : UNIT_IPV6) - 1,ptr)));
1482 return;
1483 default:
1484 fatal("Trying to copy buffer not containing valid domain, [%d,%d].",
1485 (int) out[0], (int) out[1]);
1486 }
1487 }
1488
domaineq(AddrByte * d1,AddrByte * d2)1489 static int domaineq(AddrByte *d1, AddrByte *d2)
1490 {
1491 if (d1[0] != d2[0] || d1[1] != d2[1]) {
1492 return 0;
1493 }
1494 switch (d1[0]) {
1495 case OP_GETHOSTBYNAME:
1496 return !strcmp((char*)d1+2,(char*)d2+2);
1497 case OP_GETHOSTBYADDR:
1498 return !memcmp(d1+2,d2+2, ((d1[1] == PROTO_IPV4)
1499 ? UNIT_IPV4 : UNIT_IPV6) - 1);
1500 default:
1501 fatal("Trying to compare buffers not containing valid domain, "
1502 "[%d,%d].",
1503 (int) d1[0], (int) d1[1]);
1504 return -1; /* Lint... */
1505 }
1506 }
1507
get_domainname(AddrByte * inbuff,int insize,AddrByte * domainbuff)1508 static int get_domainname(AddrByte *inbuff, int insize, AddrByte *domainbuff)
1509 {
1510 OpType op = get_op(inbuff);
1511 ProtoType proto;
1512 int i;
1513 AddrByte *data;
1514
1515 data = get_data(inbuff);
1516 switch (op) {
1517 case OP_GETHOSTBYNAME:
1518 data = get_data(inbuff);
1519 for (i = (data - inbuff); i < insize && inbuff[i] != '\0'; ++i)
1520 ;
1521 if (i < insize) {
1522 domaincopy(domainbuff, get_op_addr(inbuff));
1523 return 0;
1524 }
1525 DEBUGF(3, ("Could not pick valid domainname in "
1526 "gethostbyname operation"));
1527 return -1;
1528 case OP_GETHOSTBYADDR:
1529 proto = get_proto(inbuff);
1530 i = insize - (data - inbuff);
1531 if ((proto == PROTO_IPV4 && i == UNIT_IPV4) ||
1532 (proto == PROTO_IPV6 && i == UNIT_IPV6)) {
1533 /* An address buffer */
1534 domaincopy(domainbuff, get_op_addr(inbuff));
1535 return 0;
1536 }
1537 DEBUGF(3, ("Could not pick valid domainname in gethostbyaddr "
1538 "operation"));
1539 return -1;
1540 default:
1541 DEBUGF(2, ("Could not pick valid domainname because of "
1542 "invalid opcode %d.", (int) op));
1543 return -1;
1544 }
1545 }
1546
1547 /*
1548 * Worker subprocesses with utilities
1549 */
1550 #ifdef WIN32
create_worker(Worker * pworker,int save_que)1551 static int create_worker(Worker *pworker, int save_que)
1552 {
1553 MesQ **thread_data = ALLOC(2*sizeof(MesQ *));
1554 DWORD tid;
1555
1556
1557 if (!create_mesq(thread_data)) {
1558 fatal("Could not create, pipes for subprocess, errno = %d",
1559 GetLastError());
1560 }
1561 if (!create_mesq(thread_data + 1)) {
1562 fatal("Could not create, pipes for subprocess, errno = %d",
1563 GetLastError());
1564 }
1565 /* Save those before the thread starts */
1566 pworker->writeto = thread_data[0];
1567 pworker->readfrom = thread_data[1];
1568
1569 if (((HANDLE) _beginthreadex(NULL, 0, worker_loop, thread_data, 0, &tid))
1570 == NULL) {
1571 fatal("Could not create thread errno = %d",
1572 GetLastError());
1573 }
1574 pworker->pid = tid;
1575 pworker->state = WORKER_FREE;
1576 pworker->serial = INVALID_SERIAL;
1577 if (!save_que) {
1578 pworker->que_first = pworker->que_last = NULL;
1579 pworker->que_size = 0;
1580 }
1581 DEBUGF(3,("Created worker[%ld] with fd %d",
1582 (long) pworker->pid, (int) pworker->readfrom));
1583 return 0;
1584 }
1585
1586 #else
1587
create_worker(Worker * pworker,int save_que)1588 static int create_worker(Worker *pworker, int save_que)
1589 {
1590 int p0[2], p1[2];
1591 pid_t child;
1592
1593 if (pipe(p0)) {
1594 warning("Could not create, pipes for subprocess, errno = %d",
1595 errno);
1596 return -1;
1597 }
1598
1599 if (pipe(p1)) {
1600 warning("Could not create, pipes for subprocess, errno = %d",
1601 errno);
1602 close(p0[0]);
1603 close(p0[1]);
1604 return -1;
1605 }
1606 if ((child = fork()) < 0) { /* failure */
1607 warning("Could not fork(), errno = %d",
1608 errno);
1609 close(p0[0]);
1610 close(p0[1]);
1611 close(p1[0]);
1612 close(p1[1]);
1613 return -1;
1614 } else if (child > 0) { /* parent */
1615 close(p0[1]);
1616 close(p1[0]);
1617 pworker->writeto = p1[1];
1618 pworker->readfrom = p0[0];
1619 pworker->pid = child;
1620 pworker->state = WORKER_FREE;
1621 pworker->serial = INVALID_SERIAL;
1622 if (!save_que) {
1623 pworker->que_first = pworker->que_last = NULL;
1624 pworker->que_size = 0;
1625 }
1626 DEBUGF(3,("Created worker[%ld] with fd %d",
1627 (long) pworker->pid, (int) pworker->readfrom));
1628 return 0;
1629 } else { /* child */
1630 close(p1[1]);
1631 close(p0[0]);
1632 close_all_worker_fds();
1633 /* Make "fatal" not find any children */
1634 num_busy_workers = num_free_workers = num_stalled_workers = 0;
1635 if((dup2(p1[0],0) < 0) || (dup2(p0[1],1) < 0)) {
1636 fatal("Worker could not dup2(), errno = %d",
1637 errno);
1638 return -1; /* lint... */
1639 }
1640 close(p1[0]);
1641 close(p0[1]);
1642 signal(SIGCHLD, SIG_IGN);
1643 return worker_loop();
1644 }
1645 }
1646
close_all_worker_fds(void)1647 static void close_all_worker_fds(void)
1648 {
1649 int w,i;
1650 Worker *workers[3] = {free_workers, busy_workers, stalled_workers};
1651 int wsizes[3] = {num_free_workers, num_busy_workers,
1652 num_stalled_workers};
1653 for (w = 0; w < 3; ++w) {
1654 for (i = 0; i < wsizes[w]; ++i) {
1655 if (workers[w][i].state != WORKER_EMPTY) {
1656 close(workers[w][i].readfrom);
1657 close(workers[w][i].writeto);
1658 }
1659 }
1660 }
1661 }
1662
1663 #endif /* !WIN32 */
1664
1665 #ifdef WIN32
worker_loop(void * v)1666 DWORD WINAPI worker_loop(void *v)
1667 #else
1668 static int worker_loop(void)
1669 #endif
1670 {
1671 AddrByte *req = NULL;
1672 size_t req_size = 0;
1673 int this_size;
1674 AddrByte *reply = NULL;
1675 size_t reply_size = 0;
1676 size_t data_size;
1677
1678 #ifdef WIN32
1679 QueItem *m = NULL;
1680 MesQ *readfrom = ((MesQ **) v)[0];
1681 MesQ *writeto = ((MesQ **) v)[1];
1682 /* XXX:PaN */
1683 FREE(v);
1684 #endif
1685
1686 for(;;) {
1687 #ifdef HAVE_GETADDRINFO
1688 struct addrinfo *ai = NULL;
1689 #endif
1690 struct hostent *he = NULL;
1691 #ifdef HAVE_GETNAMEINFO
1692 struct sockaddr *sa = NULL;
1693 char name[NI_MAXHOST];
1694 #endif
1695 #if defined(HAVE_GETIPNODEBYNAME) || defined(HAVE_GETIPNODEBYADDR)
1696 int free_he = 0;
1697 #endif
1698 int error_num = 0;
1699 SerialType serial;
1700 OpType op;
1701 ProtoType proto;
1702 AddrByte *data;
1703
1704 #ifdef WIN32
1705 WaitForSingleObject(event_mesq(readfrom),INFINITE);
1706 DEBUGF(4,("Worker got data on message que."));
1707
1708 if(!deque_mesq(readfrom,&m)) {
1709 goto fail;
1710 }
1711 this_size = m->req_size;
1712 req = m->request;
1713 #else
1714 if (READ_PACKET_BYTES(0,&this_size) != PACKET_BYTES) {
1715 DEBUGF(2,("Worker got error/EOF while reading size, exiting."));
1716 exit(0);
1717 }
1718 if (this_size > req_size) {
1719 if (req == NULL) {
1720 req = ALLOC((req_size = this_size));
1721 } else {
1722 req = REALLOC(req, (req_size = this_size));
1723 }
1724 }
1725 if (read_exact(0, req, (size_t) this_size) != this_size) {
1726 DEBUGF(1,("Worker got EOF while reading data, exiting."));
1727 exit(0);
1728 }
1729 #endif
1730 /* Decode the request... */
1731 serial = get_serial(req);
1732 if (OP_CONTROL == (op = get_op(req))) {
1733 CtlType ctl;
1734 if (serial != INVALID_SERIAL) {
1735 DEBUGF(1, ("Worker got invalid serial: %d.", serial));
1736 exit(0);
1737 }
1738 switch (ctl = get_ctl(req)) {
1739 case SETOPT_DEBUG_LEVEL:
1740 debug_level = get_debug_level(req);
1741 DEBUGF(debug_level,
1742 ("Worker debug_level = %d.", debug_level));
1743 break;
1744 }
1745 continue;
1746 }
1747 proto = get_proto(req);
1748 data = get_data(req);
1749 DEBUGF(4,("Worker got request, op = %d, proto = %d, data = %s.",
1750 op,proto,data));
1751 /* Got a request, lets go... */
1752 switch (op) {
1753 case OP_GETHOSTBYNAME:
1754 switch (proto) {
1755
1756 #ifdef HAVE_IN6
1757 case PROTO_IPV6: { /* switch (proto) { */
1758 #ifdef HAVE_GETADDRINFO
1759 struct addrinfo hints;
1760
1761 memset(&hints, 0, sizeof(hints));
1762 hints.ai_flags = AI_CANONNAME;
1763 hints.ai_socktype = SOCK_STREAM;
1764 hints.ai_family = AF_INET6;
1765 DEBUGF(5, ("Starting getaddrinfo(%s, ...)", data));
1766 error_num = getaddrinfo((char *)data, NULL, &hints, &ai);
1767 DEBUGF(5,("getaddrinfo returned %d", error_num));
1768 if (error_num) {
1769 error_num = map_netdb_error_ai(error_num);
1770 }
1771 #elif defined(HAVE_GETIPNODEBYNAME) /*#ifdef HAVE_GETADDRINFO */
1772 DEBUGF(5,("Starting getipnodebyname(%s)",data));
1773 he = getipnodebyname(data, AF_INET6, 0, &error_num);
1774 if (he) {
1775 free_he = 1;
1776 error_num = 0;
1777 DEBUGF(5,("getipnodebyname(,AF_INET6,,) OK"));
1778 } else {
1779 DEBUGF(5,("getipnodebyname(,AF_INET6,,) error %d", error_num));
1780 error_num = map_netdb_error(error_num);
1781 }
1782 #elif defined(HAVE_GETHOSTBYNAME2) /*#ifdef HAVE_GETADDRINFO */
1783 DEBUGF(5,("Starting gethostbyname2(%s, AF_INET6)",data));
1784 he = gethostbyname2((char*)data, AF_INET6);
1785 if (he) {
1786 error_num = 0;
1787 DEBUGF(5,("gethostbyname2(, AF_INET6) OK"));
1788 } else {
1789 error_num = map_netdb_error(h_errno);
1790 DEBUGF(5,("gethostbyname2(, AF_INET6) error %d", h_errno));
1791 }
1792 #else
1793 error_num = ERRCODE_NOTSUP;
1794 #endif /*#ifdef HAVE_GETADDRINFO */
1795 } break;
1796 #endif /*ifdef HAVE_IN6 */
1797
1798 case PROTO_IPV4: { /* switch (proto) { */
1799 DEBUGF(5,("Starting gethostbyname(%s)",data));
1800 he = gethostbyname((char*)data);
1801 if (he) {
1802 error_num = 0;
1803 DEBUGF(5,("gethostbyname OK"));
1804 } else {
1805 error_num = map_netdb_error(h_errno);
1806 DEBUGF(5,("gethostbyname error %d", h_errno));
1807 }
1808 } break;
1809
1810 default: /* switch (proto) { */
1811 /* Not supported... */
1812 error_num = ERRCODE_NOTSUP;
1813 break;
1814 } /* switch (proto) { */
1815
1816 if (he) {
1817 data_size = build_reply(serial, he, &reply, &reply_size);
1818 #ifdef HAVE_GETIPNODEBYNAME
1819 if (free_he) {
1820 freehostent(he);
1821 }
1822 #endif
1823 #ifdef HAVE_GETADDRINFO
1824 } else if (ai) {
1825 data_size = build_reply_ai(serial, 16, ai,
1826 &reply, &reply_size);
1827 freeaddrinfo(ai);
1828 #endif
1829 } else {
1830 data_size = build_error_reply(serial, error_num,
1831 &reply, &reply_size);
1832 }
1833 break; /* case OP_GETHOSTBYNAME: */
1834
1835 case OP_GETHOSTBYADDR: /* switch (op) { */
1836 switch (proto) {
1837 #ifdef HAVE_IN6
1838 case PROTO_IPV6: {
1839 #ifdef HAVE_GETNAMEINFO
1840 struct sockaddr_in6 *sin;
1841 socklen_t salen = sizeof(*sin);
1842
1843 sin = ALLOC(salen);
1844 #ifndef NO_SA_LEN
1845 sin->sin6_len = salen;
1846 #endif
1847 sin->sin6_family = AF_INET6;
1848 sin->sin6_port = 0;
1849 memcpy(&sin->sin6_addr, data, 16);
1850 sa = (struct sockaddr *)sin;
1851 DEBUGF(5,("Starting getnameinfo(,,%s,16,,,)",
1852 format_address(16, data)));
1853 error_num = getnameinfo(sa, salen, name, sizeof(name),
1854 NULL, 0, NI_NAMEREQD);
1855 DEBUGF(5,("getnameinfo returned %d", error_num));
1856 if (error_num) {
1857 error_num = map_netdb_error_ai(error_num);
1858 sa = NULL;
1859 }
1860 #elif defined(HAVE_GETIPNODEBYADDR) /*#ifdef HAVE_GETNAMEINFO*/
1861 struct in6_addr ia;
1862 memcpy(ia.s6_addr, data, 16);
1863 DEBUGF(5,("Starting getipnodebyaddr(%s,16,AF_INET6,)",
1864 format_address(16, data)));
1865 he = getipnodebyaddr(&ia, 16, AF_INET6, &error_num);
1866 free_he = 1;
1867 if (! he) {
1868 DEBUGF(5,("getipnodebyaddr error %d", error_num));
1869 error_num = map_netdb_error(error_num);
1870 } else {
1871 DEBUGF(5,("getipnodebyaddr OK"));
1872 }
1873 #else /*#ifdef HAVE_GETNAMEINFO*/
1874 struct in6_addr ia;
1875 memcpy(ia.s6_addr, data, 16);
1876 DEBUGF(5,("Starting gethostbyaddr(%s,16,AF_INET6)",
1877 format_address(16, data)));
1878 he = gethostbyaddr((const char *) &ia, 16, AF_INET6);
1879 if (! he) {
1880 error_num = map_netdb_error(h_errno);
1881 DEBUGF(5,("gethostbyaddr error %d", h_errno));
1882 } else {
1883 DEBUGF(5,("gethostbyaddr OK"));
1884 }
1885 #endif /* #ifdef HAVE_GETNAMEINFO */
1886 } break; /* case PROTO_IPV6: { */
1887 #endif /* #ifdef HAVE_IN6 */
1888
1889 case PROTO_IPV4: { /* switch(proto) { */
1890 struct in_addr ia;
1891 memcpy(&ia.s_addr, data, 4); /* Alignment required... */
1892 DEBUGF(5,("Starting gethostbyaddr(%s,4,AF_INET)",
1893 format_address(4, data)));
1894 he = gethostbyaddr((const char *) &ia, 4, AF_INET);
1895 if (! he) {
1896 error_num = map_netdb_error(h_errno);
1897 DEBUGF(5,("gethostbyaddr error %d", h_errno));
1898 } else {
1899 DEBUGF(5,("gethostbyaddr OK"));
1900 }
1901 } break;
1902
1903 default:
1904 error_num = ERRCODE_NOTSUP;
1905 } /* switch(proto) { */
1906
1907 if (he) {
1908 data_size = build_reply(serial, he, &reply, &reply_size);
1909 #ifdef HAVE_GETIPNODEBYADDR
1910 if (free_he) {
1911 freehostent(he);
1912 }
1913 #endif
1914 #ifdef HAVE_GETNAMEINFO
1915 } else if (sa) {
1916 struct addrinfo res;
1917 memset(&res, 0, sizeof(res));
1918 res.ai_canonname = name;
1919 res.ai_addr = sa;
1920 res.ai_next = NULL;
1921 data_size = build_reply_ai(serial, 16, &res,
1922 &reply, &reply_size);
1923 free(sa);
1924 #endif
1925 } else {
1926 data_size = build_error_reply(serial, error_num,
1927 &reply, &reply_size);
1928 }
1929 break; /* case OP_GETHOSTBYADR: */
1930
1931 default:
1932 data_size = build_error_reply(serial, ERRCODE_NOTSUP,
1933 &reply, &reply_size);
1934 break;
1935 } /* switch (op) { */
1936
1937 #ifdef WIN32
1938 m = REALLOC(m, sizeof(QueItem) - 1 + data_size - PACKET_BYTES);
1939 m->next = NULL;
1940 m->req_size = data_size - PACKET_BYTES;
1941 memcpy(m->request,reply + PACKET_BYTES,data_size - PACKET_BYTES);
1942 if (!enque_mesq(writeto,m)) {
1943 goto fail;
1944 }
1945 m = NULL;
1946 #else
1947 /* expect no signals */
1948 if (write(1, reply, data_size) < 0)
1949 goto fail;
1950 #endif
1951 } /* for (;;) */
1952
1953 fail:
1954 #ifdef WIN32
1955 if (m != NULL) {
1956 FREE(m);
1957 }
1958 close_mesq(readfrom);
1959 close_mesq(writeto);
1960 if (reply) {
1961 FREE(reply);
1962 }
1963 #endif
1964 return 1;
1965 }
1966
map_netdb_error(int netdb_code)1967 static int map_netdb_error(int netdb_code)
1968 {
1969 switch (netdb_code) {
1970 #ifdef HOST_NOT_FOUND
1971 case HOST_NOT_FOUND:
1972 return ERRCODE_HOST_NOT_FOUND;
1973 #endif
1974 #ifdef TRY_AGAIN
1975 case TRY_AGAIN:
1976 return ERRCODE_TRY_AGAIN;
1977 #endif
1978 #ifdef NO_RECOVERY
1979 case NO_RECOVERY:
1980 return ERRCODE_NO_RECOVERY;
1981 #endif
1982 #if defined(NO_DATA) || defined(NO_ADDRESS)
1983 #ifdef NO_DATA
1984 case NO_DATA:
1985 #endif
1986 #ifdef NO_ADDRESS
1987 #if !defined(NO_DATA) || (NO_DATA != NO_ADDRESS)
1988 case NO_ADDRESS:
1989 #endif
1990 #endif
1991 return ERRCODE_NO_DATA;
1992 #endif
1993 default:
1994 return ERRCODE_NETDB_INTERNAL;
1995 }
1996 }
1997
1998 #if defined(HAVE_GETADDRINFO) || defined(HAVE_GETNAMEINFO)
map_netdb_error_ai(int netdb_code)1999 static int map_netdb_error_ai(int netdb_code)
2000 {
2001 switch(netdb_code) {
2002 #ifdef EAI_ADDRFAMILY
2003 case EAI_ADDRFAMILY:
2004 return ERRCODE_NETDB_INTERNAL;
2005 #endif
2006 case EAI_AGAIN:
2007 return ERRCODE_TRY_AGAIN;
2008 case EAI_BADFLAGS:
2009 return ERRCODE_NETDB_INTERNAL;
2010 case EAI_FAIL:
2011 return ERRCODE_HOST_NOT_FOUND;
2012 case EAI_FAMILY:
2013 return ERRCODE_NETDB_INTERNAL;
2014 case EAI_MEMORY:
2015 return ERRCODE_NETDB_INTERNAL;
2016 #if defined(EAI_NODATA) && EAI_NODATA != EAI_NONAME
2017 case EAI_NODATA:
2018 return ERRCODE_HOST_NOT_FOUND;
2019 #endif
2020 case EAI_NONAME:
2021 return ERRCODE_HOST_NOT_FOUND;
2022 case EAI_SERVICE:
2023 return ERRCODE_NETDB_INTERNAL;
2024 case EAI_SOCKTYPE:
2025 return ERRCODE_NETDB_INTERNAL;
2026 default:
2027 return ERRCODE_NETDB_INTERNAL;
2028 }
2029 }
2030 #endif /* #if defined(HAVE_GETADDRINFO) || defined(HAVE_GETNAMEINFO) */
2031
2032
errcode_to_string(int errcode)2033 static char *errcode_to_string(int errcode)
2034 {
2035 switch (errcode) {
2036 case ERRCODE_NOTSUP:
2037 return "enotsup";
2038 case ERRCODE_HOST_NOT_FOUND:
2039 /*
2040 * I would preffer
2041 * return "host_not_found";
2042 * but have to keep compatibility with the old
2043 * inet_gethost's error codes...
2044 */
2045 return "notfound";
2046 case ERRCODE_TRY_AGAIN:
2047 return "try_again";
2048 case ERRCODE_NO_RECOVERY:
2049 return "no_recovery";
2050 case ERRCODE_NO_DATA:
2051 return "no_data";
2052 default:
2053 /*case ERRCODE_NETDB_INTERNAL:*/
2054 return "netdb_internal";
2055 }
2056 }
2057
build_error_reply(SerialType serial,int errnum,AddrByte ** preply,size_t * preply_size)2058 static size_t build_error_reply(SerialType serial, int errnum,
2059 AddrByte **preply,
2060 size_t *preply_size)
2061 {
2062 char *errstring = errcode_to_string(errnum);
2063 int string_need = strlen(errstring) + 1; /* a '\0' too */
2064 unsigned need;
2065 AddrByte *ptr;
2066
2067 need = PACKET_BYTES + 4 /* Serial */ + 1 /* Unit */ + string_need;
2068 if (*preply_size < need) {
2069 if (*preply_size == 0) {
2070 *preply = ALLOC((*preply_size = need));
2071 } else {
2072 *preply = REALLOC(*preply,
2073 (*preply_size = need));
2074 }
2075 }
2076 ptr = *preply;
2077 PUT_PACKET_BYTES(ptr,need - PACKET_BYTES);
2078 ptr += PACKET_BYTES;
2079 put_int32(ptr,serial);
2080 ptr +=4;
2081 *ptr++ = (AddrByte) 0; /* 4 or 16 */
2082 strcpy((char*)ptr, errstring);
2083 return need;
2084 }
2085
2086
2087
build_reply(SerialType serial,struct hostent * he,AddrByte ** preply,size_t * preply_size)2088 static size_t build_reply(SerialType serial, struct hostent *he,
2089 AddrByte **preply, size_t *preply_size)
2090 {
2091 unsigned need;
2092 int strings_need;
2093 int num_strings;
2094 int num_addresses;
2095 int i;
2096 AddrByte *ptr;
2097 int unit = he->h_length;
2098
2099 for (num_addresses = 0; he->h_addr_list[num_addresses] != NULL;
2100 ++num_addresses)
2101 ;
2102 strings_need = strlen(he->h_name) + 1; /* 1 for null byte */
2103 num_strings = 1;
2104 if (he->h_aliases) {
2105 for(i=0; he->h_aliases[i] != NULL; ++i) {
2106 strings_need += strlen(he->h_aliases[i]) + 1;
2107 ++num_strings;
2108 }
2109 }
2110
2111 need = PACKET_BYTES +
2112 4 /* Serial */ + 1 /* Unit */ + 4 /* Naddr */ +
2113 (unit * num_addresses) /* Address bytes */ +
2114 4 /* Nnames */ + strings_need /* The name and alias strings */;
2115
2116 if (*preply_size < need) {
2117 if (*preply_size == 0) {
2118 *preply = ALLOC((*preply_size = need));
2119 } else {
2120 *preply = REALLOC(*preply,
2121 (*preply_size = need));
2122 }
2123 }
2124 ptr = *preply;
2125 PUT_PACKET_BYTES(ptr,need - PACKET_BYTES);
2126 ptr += PACKET_BYTES;
2127 put_int32(ptr,serial);
2128 ptr +=4;
2129 *ptr++ = (AddrByte) unit; /* 4 or 16 */
2130 put_int32(ptr, num_addresses);
2131 ptr += 4;
2132 for (i = 0; i < num_addresses; ++i) {
2133 memcpy(ptr, he->h_addr_list[i], unit);
2134 ptr += unit;
2135 }
2136 put_int32(ptr, num_strings);
2137 ptr += 4;
2138 strcpy((char*)ptr, he->h_name);
2139 ptr += 1 + strlen(he->h_name);
2140 for (i = 0; i < (num_strings - 1); ++i) {
2141 strcpy((char*)ptr, he->h_aliases[i]);
2142 ptr += 1 + strlen(he->h_aliases[i]);
2143 }
2144 return need;
2145 }
2146
2147 #if defined(HAVE_GETADDRINFO) || defined(HAVE_GETNAMEINFO)
build_reply_ai(SerialType serial,int addrlen,struct addrinfo * res0,AddrByte ** preply,size_t * preply_size)2148 static size_t build_reply_ai(SerialType serial, int addrlen,
2149 struct addrinfo *res0,
2150 AddrByte **preply, size_t *preply_size)
2151 {
2152 struct addrinfo *res;
2153 int num_strings;
2154 int num_addresses;
2155 AddrByte *ptr;
2156 int need;
2157
2158 num_addresses = 0;
2159 num_strings = 0;
2160 need = PACKET_BYTES +
2161 4 /* Serial */ + 1 /* addrlen */ +
2162 4 /* Naddr */ + 4 /* Nnames */;
2163
2164 for (res = res0; res != NULL; res = res->ai_next) {
2165 if (res->ai_addr) {
2166 num_addresses++;
2167 need += addrlen;
2168 }
2169 if (res->ai_canonname) {
2170 num_strings++;
2171 need += strlen(res->ai_canonname) + 1;
2172 }
2173 }
2174
2175 if (*preply_size < need) {
2176 if (*preply_size == 0) {
2177 *preply = ALLOC((*preply_size = need));
2178 } else {
2179 *preply = REALLOC(*preply,
2180 (*preply_size = need));
2181 }
2182 }
2183
2184 ptr = *preply;
2185 PUT_PACKET_BYTES(ptr,need - PACKET_BYTES);
2186 ptr += PACKET_BYTES;
2187 put_int32(ptr,serial);
2188 ptr +=4;
2189 *ptr++ = (AddrByte) addrlen; /* 4 or 16 */
2190 put_int32(ptr, num_addresses);
2191 ptr += 4;
2192 for (res = res0; res != NULL && num_addresses; res = res->ai_next) {
2193 if (res->ai_addr == NULL)
2194 continue;
2195 if (addrlen == 4)
2196 memcpy(ptr, &((struct sockaddr_in *)res->ai_addr)->sin_addr, addrlen);
2197 #ifdef AF_INET6
2198 else if (addrlen == 16)
2199 memcpy(ptr, &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, addrlen);
2200 #endif
2201 else
2202 memcpy(ptr, res->ai_addr->sa_data, addrlen);
2203 ptr += addrlen;
2204 num_addresses--;
2205 }
2206 put_int32(ptr, num_strings);
2207 ptr += 4;
2208 for (res = res0; res != NULL && num_strings; res = res->ai_next) {
2209 if (res->ai_canonname == NULL)
2210 continue;
2211 strcpy((char *)ptr, res->ai_canonname);
2212 ptr += strlen(res->ai_canonname) + 1;
2213 num_strings--;
2214 }
2215 return need;
2216 }
2217
2218 #endif /* #if defined(HAVE_GETADDRINFO) || defined(HAVE_GETNAMEINFO) */
2219
2220
2221
2222 /*
2223 * Encode/decode/read/write
2224 */
2225
get_int32(AddrByte * b)2226 static int get_int32(AddrByte *b)
2227 {
2228 int res;
2229 res = (unsigned) b[3];
2230 res |= ((unsigned) b[2]) << 8;
2231 res |= ((unsigned) b[1]) << 16;
2232 res |= ((unsigned) b[0]) << 24;
2233 return res;
2234 }
2235
put_int32(AddrByte * buff,int value)2236 static void put_int32(AddrByte *buff, int value)
2237 {
2238 buff[0] = (((unsigned) value) >> 24) & 0xFF;
2239 buff[1] = (((unsigned) value) >> 16) & 0xFF;
2240 buff[2] = (((unsigned) value) >> 8) & 0xFF;
2241 buff[3] = ((unsigned) value) & 0xFF;
2242 }
2243 #ifdef WIN32
2244
read_int32(HANDLE fd,int * res,HANDLE ev)2245 static int read_int32(HANDLE fd, int *res, HANDLE ev)
2246 {
2247 AddrByte b[4];
2248 int r;
2249 if ((r = read_exact(fd,b,4,ev)) < 0) {
2250 return -1;
2251 } else if (r == 0) {
2252 return 0;
2253 } else {
2254 *res = (unsigned) b[3];
2255 *res |= ((unsigned) b[2]) << 8;
2256 *res |= ((unsigned) b[1]) << 16;
2257 *res |= ((unsigned) b[0]) << 24;
2258 }
2259 return 4;
2260 }
2261 /*
2262 * The standard input is expected to be opened with FILE_FLAG_OVERLAPPED
2263 * but this code should handle both cases (although winsock might not).
2264 */
read_exact(HANDLE fd,void * vbuff,DWORD nbytes,HANDLE ev)2265 static int read_exact(HANDLE fd, void *vbuff, DWORD nbytes, HANDLE ev)
2266 {
2267 DWORD ret,got;
2268 BOOL stat;
2269 char *buff = vbuff;
2270 OVERLAPPED ov;
2271 DWORD err;
2272
2273
2274 got = 0;
2275 for(;;) {
2276 memset(&ov,0,sizeof(ov));
2277 ov.hEvent = ev;
2278 ResetEvent(ov.hEvent);
2279 stat = ReadFile(fd, buff, nbytes - got, &ret, &ov);
2280 if (!stat) {
2281 if ((err = GetLastError()) == ERROR_IO_PENDING) {
2282 DEBUGF(4,("Overlapped read, waiting for completion..."));
2283 WaitForSingleObject(ov.hEvent,INFINITE);
2284 stat = GetOverlappedResult(fd,&ov,&ret,TRUE);
2285 DEBUGF(4,("Overlapped read, completed with status %d,"
2286 " result %d",stat,ret));
2287 }
2288 if (!stat) {
2289 if (GetLastError() == ERROR_BROKEN_PIPE) {
2290 DEBUGF(1, ("End of file while reading from pipe."));
2291 return 0;
2292 } else {
2293 DEBUGF(1, ("Error while reading from pipe,"
2294 " errno = %d",
2295 GetLastError()));
2296 return -1;
2297 }
2298 }
2299 } else {
2300 DEBUGF(4,("Read completed syncronously, result %d",ret));
2301 }
2302 if (ret == 0) {
2303 DEBUGF(1, ("End of file detected as zero read from pipe."));
2304 return 0;
2305 }
2306 if (ret < nbytes - got) {
2307 DEBUGF(4,("Not all data read from pipe, still %d bytes to read.",
2308 nbytes - (got + ret)));
2309 got += ret;
2310 buff += ret;
2311 } else {
2312 return nbytes;
2313 }
2314 }
2315 }
2316 /*
2317 * Now, we actually expect a HANDLE opened with FILE_FLAG_OVERLAPPED,
2318 * but this code should handle both cases (although winsock
2319 * does not always..)
2320 */
write_exact(HANDLE fd,AddrByte * buff,DWORD len,HANDLE ev)2321 static int write_exact(HANDLE fd, AddrByte *buff, DWORD len, HANDLE ev)
2322 {
2323 DWORD res,stat;
2324 DWORD x = len;
2325 OVERLAPPED ov;
2326 DWORD err;
2327
2328
2329 for(;;) {
2330 memset(&ov,0,sizeof(ov));
2331 ov.hEvent = ev;
2332 ResetEvent(ov.hEvent);
2333 stat = WriteFile(fd,buff,x,&res,&ov);
2334 if (!stat) {
2335 if ((err = GetLastError()) == ERROR_IO_PENDING) {
2336 DEBUGF(4,("Overlapped write, waiting for competion..."));
2337 WaitForSingleObject(ov.hEvent,INFINITE);
2338 stat = GetOverlappedResult(fd,&ov,&res,TRUE);
2339 DEBUGF(4,("Overlapped write, completed with status %d,"
2340 " result %d",stat,res));
2341 }
2342 if (!stat) {
2343 if (GetLastError() == ERROR_BROKEN_PIPE) {
2344 return 0;
2345 } else {
2346 return -1;
2347 }
2348 }
2349 } else {
2350 DEBUGF(4,("Write completed syncronously, result %d",res));
2351 }
2352
2353 if (res < x) {
2354 /* Microsoft states this can happen as HANDLE is a pipe... */
2355 DEBUGF(4,("Not all data written to pipe, still %d bytes to write.",
2356 x - res));
2357 x -= res;
2358 buff += res;
2359 } else {
2360 return len;
2361 }
2362 }
2363 }
2364
reader(void * data)2365 DWORD WINAPI reader(void *data) {
2366 MesQ *mq = (MesQ *) data;
2367 QueItem *m;
2368 int siz;
2369 int r;
2370 HANDLE inp;
2371 int x = 0;
2372 HANDLE ev = CreateEvent(NULL, TRUE, FALSE, NULL);
2373
2374 inp = GetStdHandle(STD_INPUT_HANDLE);
2375 for (;;) {
2376 if ((r = READ_PACKET_BYTES(inp,&siz,ev)) != 4) {
2377 DEBUGF(1,("Erlang has closed (reading)"));
2378 exit(0);
2379 }
2380 DEBUGF(4,("Read packet of size %d from erlang",siz));
2381 m = ALLOC(sizeof(QueItem) - 1 + siz);
2382 if (read_exact(inp, m->request, siz,ev) != siz) {
2383 fatal("Unexpected end of file on main input, errno = %d",errno);
2384 }
2385 if (siz < 5) {
2386 fatal("Unexpected message on main input, message size %d less "
2387 "than minimum.");
2388 }
2389 m->req_size = siz;
2390 m->next = NULL;
2391 if (!enque_mesq(mq, m)) {
2392 fatal("Reader could not talk to main thread!");
2393 }
2394 }
2395 }
2396
writer(void * data)2397 DWORD WINAPI writer(void *data)
2398 {
2399 MesQ *mq = (MesQ *) data;
2400 QueItem *m;
2401 HANDLE outp = GetStdHandle(STD_OUTPUT_HANDLE);
2402 AddrByte hdr[PACKET_BYTES];
2403 HANDLE ev = CreateEvent(NULL, TRUE, FALSE, NULL);
2404
2405
2406 for (;;) {
2407 WaitForSingleObject(event_mesq(mq),INFINITE);
2408 if (!deque_mesq(mq, &m)) {
2409 fatal("Writer could not talk to main thread!");
2410 }
2411 PUT_PACKET_BYTES(hdr, m->req_size);
2412 if (write_exact(outp, hdr, 4, ev) != 4) {
2413 DEBUGF(1,("Erlang has closed (writing)"));
2414 exit(0);
2415 }
2416 if (write_exact(outp, m->request, m->req_size, ev) != m->req_size) {
2417 DEBUGF(1,("Erlang has closed (writing)"));
2418 exit(0);
2419 }
2420 FREE(m);
2421 }
2422 }
2423
2424
2425 #else
2426
read_int32(int fd,int * res)2427 static size_t read_int32(int fd, int *res)
2428 {
2429 AddrByte b[4];
2430 int r;
2431 if ((r = read_exact(fd,b,4)) < 0) {
2432 return -1;
2433 } else if (r == 0) {
2434 return 0;
2435 } else {
2436 *res = (unsigned) b[3];
2437 *res |= ((unsigned) b[2]) << 8;
2438 *res |= ((unsigned) b[1]) << 16;
2439 *res |= ((unsigned) b[0]) << 24;
2440 }
2441 return 4;
2442 }
2443
read_exact(int fd,void * vbuff,size_t nbytes)2444 static ssize_t read_exact(int fd, void *vbuff, size_t nbytes)
2445 {
2446 ssize_t ret, got;
2447 char *buff = vbuff;
2448
2449 got = 0;
2450 for(;;) {
2451 ret = read(fd, buff, nbytes - got);
2452 if (ret < 0) {
2453 if (errno == EINTR) {
2454 continue;
2455 } else {
2456 DEBUGF(1, ("Error while reading from pipe,"
2457 " errno = %d",
2458 errno));
2459 return -1;
2460 }
2461 } else if (ret == 0) {
2462 DEBUGF(1, ("End of file while reading from pipe."));
2463 if (got == 0) {
2464 return 0; /* "Normal" EOF */
2465 } else {
2466 return -1;
2467 }
2468 } else if (ret < nbytes - got) {
2469 got += ret;
2470 buff += ret;
2471 } else {
2472 return nbytes;
2473 }
2474 }
2475 }
2476
write_exact(int fd,AddrByte * buff,int len)2477 static int write_exact(int fd, AddrByte *buff, int len)
2478 {
2479 int res;
2480 int x = len;
2481 for(;;) {
2482 if((res = write(fd, buff, x)) == x) {
2483 break;
2484 }
2485 if (res < 0) {
2486 if (errno == EINTR) {
2487 continue;
2488 } else if (errno == EPIPE) {
2489 return 0;
2490 }
2491 #ifdef ENXIO
2492 else if (errno == ENXIO) {
2493 return 0;
2494 }
2495 #endif
2496 else {
2497 return -1;
2498 }
2499 } else {
2500 /* Hmmm, blocking write but not all written, could this happen
2501 if the other end was closed during the operation? Well,
2502 it costs very little to handle anyway... */
2503 x -= res;
2504 buff += res;
2505 }
2506 }
2507 return len;
2508 }
2509
2510 #endif /* !WIN32 */
2511
2512 /*
2513 * Debug and memory allocation
2514 */
2515
format_address(int siz,AddrByte * addr)2516 static char *format_address(int siz, AddrByte *addr)
2517 {
2518 static char buff[50];
2519 char tmp[10];
2520 if (siz > 16) {
2521 return "(unknown)";
2522 }
2523 *buff='\0';
2524 if (siz <= 4) {
2525 while(siz--) {
2526 erts_snprintf(tmp, sizeof(tmp), "%d",(int) *addr++);
2527 strcat(buff,tmp);
2528 if(siz) {
2529 strcat(buff,".");
2530 }
2531 }
2532 return buff;
2533 }
2534 while(siz--) {
2535 erts_snprintf(tmp, sizeof(tmp), "%02x",(int) *addr++);
2536 strcat(buff,tmp);
2537 if(siz) {
2538 strcat(buff,":");
2539 }
2540 }
2541 return buff;
2542 }
2543
debugf(char * format,...)2544 static void debugf(char *format, ...)
2545 {
2546 char buff[2048];
2547 char *ptr;
2548 va_list ap;
2549
2550 va_start(ap,format);
2551 #ifdef WIN32
2552 erts_snprintf(buff, sizeof(buff), "%s[%d] (DEBUG):",program_name,(int) GetCurrentThreadId());
2553 #else
2554 erts_snprintf(buff, sizeof(buff), "%s[%d] (DEBUG):",program_name,(int) getpid());
2555 #endif
2556 ptr = buff + strlen(buff);
2557 erts_vsnprintf(ptr,sizeof(buff)-strlen(buff)-2,format,ap);
2558 strcat(ptr,"\r\n");
2559 #ifdef WIN32
2560 if (debug_console_allocated != INVALID_HANDLE_VALUE) {
2561 DWORD res;
2562 WriteFile(debug_console_allocated,buff,strlen(buff),&res,NULL);
2563 }
2564 #else
2565 /* suppress warning with 'if' */
2566 if(write(2,buff,strlen(buff)))
2567 ;
2568 #endif
2569 va_end(ap);
2570 }
2571
warning(char * format,...)2572 static void warning(char *format, ...)
2573 {
2574 char buff[2048];
2575 char *ptr;
2576 va_list ap;
2577
2578 va_start(ap,format);
2579 erts_snprintf(buff, sizeof(buff), "%s[%d]: WARNING:",program_name, (int) getpid());
2580 ptr = buff + strlen(buff);
2581 erts_vsnprintf(ptr,sizeof(buff)-strlen(buff)-2,format,ap);
2582 strcat(ptr,"\r\n");
2583 #ifdef WIN32
2584 {
2585 DWORD res;
2586 WriteFile(GetStdHandle(STD_ERROR_HANDLE),buff,strlen(buff),&res,NULL);
2587 }
2588 #else
2589 /* suppress warning with 'if' */
2590 if(write(2,buff,strlen(buff)))
2591 ;
2592 #endif
2593 va_end(ap);
2594 }
2595
fatal(char * format,...)2596 static void fatal(char *format, ...)
2597 {
2598 char buff[2048];
2599 char *ptr;
2600 va_list ap;
2601
2602 va_start(ap,format);
2603 erts_snprintf(buff, sizeof(buff), "%s[%d]: FATAL ERROR:",program_name, (int) getpid());
2604 ptr = buff + strlen(buff);
2605 erts_vsnprintf(ptr,sizeof(buff)-strlen(buff)-2,format,ap);
2606 strcat(ptr,"\r\n");
2607 #ifdef WIN32
2608 {
2609 DWORD res;
2610 WriteFile(GetStdHandle(STD_ERROR_HANDLE),buff,strlen(buff),&res,NULL);
2611 }
2612 #else
2613 /* suppress warning with 'if' */
2614 if(write(2,buff,strlen(buff)))
2615 ;
2616 #endif
2617 va_end(ap);
2618 #ifndef WIN32
2619 kill_all_workers();
2620 #endif
2621 exit(1);
2622 }
2623
my_malloc(size_t size)2624 static void *my_malloc(size_t size)
2625 {
2626 void *ptr = malloc(size);
2627 if (!ptr) {
2628 fatal("Cannot allocate %u bytes of memory.", (unsigned) size);
2629 return NULL; /* lint... */
2630 }
2631 return ptr;
2632 }
2633
my_realloc(void * old,size_t size)2634 static void *my_realloc(void *old, size_t size)
2635 {
2636 void *ptr = realloc(old, size);
2637 if (!ptr) {
2638 fatal("Cannot reallocate %u bytes of memory from %p.",
2639 (unsigned) size, old);
2640 return NULL; /* lint... */
2641 }
2642 return ptr;
2643 }
2644
2645 #ifdef WIN32
2646
create_mesq(MesQ ** q)2647 BOOL create_mesq(MesQ **q)
2648 {
2649 MesQ *tmp = ALLOC(sizeof(MesQ));
2650 tmp->data_present = CreateEvent(NULL, TRUE, FALSE,NULL);
2651 if (tmp->data_present == NULL) {
2652 free(tmp);
2653 return FALSE;
2654 }
2655 InitializeCriticalSection(&(tmp->crit)); /* Cannot fail */
2656 tmp->shutdown = 0;
2657 tmp->first = NULL;
2658 tmp->last = NULL;
2659 *q = tmp;
2660 return TRUE;
2661 }
2662
enque_mesq(MesQ * q,QueItem * m)2663 BOOL enque_mesq(MesQ *q, QueItem *m)
2664 {
2665 EnterCriticalSection(&(q->crit));
2666 if (q->shutdown) {
2667 LeaveCriticalSection(&(q->crit));
2668 return FALSE;
2669 }
2670 if (q->last == NULL) {
2671 q->first = q->last = m;
2672 } else {
2673 q->last->next = m;
2674 q->last = m;
2675 }
2676 m->next = NULL;
2677 if (!SetEvent(q->data_present)) {
2678 fprintf(stderr,"Fatal: Unable to signal event in %s:%d, last error: %d\n",
2679 __FILE__,__LINE__,GetLastError());
2680 exit(1); /* Unable to continue at all */
2681 }
2682 LeaveCriticalSection(&(q->crit));
2683 return TRUE;
2684 }
2685
deque_mesq(MesQ * q,QueItem ** m)2686 BOOL deque_mesq(MesQ *q, QueItem **m)
2687 {
2688 EnterCriticalSection(&(q->crit));
2689 if (q->first == NULL) { /* Usually shutdown from other end */
2690 ResetEvent(q->data_present);
2691 LeaveCriticalSection(&(q->crit));
2692 return FALSE;
2693 }
2694 *m = q->first;
2695 q->first = q->first->next;
2696 if (q->first == NULL) {
2697 q->last = NULL;
2698 ResetEvent(q->data_present);
2699 }
2700 (*m)->next = NULL;
2701 LeaveCriticalSection(&(q->crit));
2702 return TRUE;
2703 }
2704
close_mesq(MesQ * q)2705 BOOL close_mesq(MesQ *q)
2706 {
2707 QueItem *tmp;
2708 EnterCriticalSection(&(q->crit));
2709 if (!q->shutdown) {
2710 q->shutdown = TRUE;
2711 if (!SetEvent(q->data_present)) {
2712 fprintf(stderr,
2713 "Fatal: Unable to signal event in %s:%d, last error: %d\n",
2714 __FILE__,__LINE__,GetLastError());
2715 exit(1); /* Unable to continue at all */
2716 }
2717 LeaveCriticalSection(&(q->crit));
2718 return FALSE;
2719 }
2720 /* No one else is supposed to use this object any more */
2721 LeaveCriticalSection(&(q->crit));
2722 DeleteCriticalSection(&(q->crit));
2723 CloseHandle(q->data_present);
2724 tmp = q->first;
2725 while(tmp) {
2726 q->first = q->first->next;
2727 free(tmp);
2728 tmp = q->first;
2729 }
2730 free(q);
2731 return TRUE;
2732 }
2733
event_mesq(MesQ * q)2734 HANDLE event_mesq(MesQ *q)
2735 {
2736 return q->data_present;
2737 }
2738
2739 #ifdef HARDDEBUG
pseudo_worker_loop(void * v)2740 DWORD WINAPI pseudo_worker_loop(void *v)
2741 {
2742 HOSTENT *hep;
2743
2744 DEBUGF(1,("gethostbyname(\"ftp.funet.fi\") starting"));
2745 hep = gethostbyname("ftp.funet.fi");
2746
2747 DEBUGF(1,("gethostbyname(\"ftp.funet.fi\") -> %d OK",(int) hep));
2748 return 0;
2749 }
2750
poll_gethost(int row)2751 static void poll_gethost(int row) {
2752 HANDLE h;
2753 DWORD tid;
2754 h = (HANDLE) _beginthreadex(NULL, 0, pseudo_worker_loop, NULL, 0, &tid);
2755 if (h == NULL) {
2756 DEBUGF(1,("Failed to spawn pseudo worker (%d)...",row));
2757 } else {
2758 DEBUGF(1,("Waiting for pseudo worker (%d)", row));
2759 WaitForSingleObject(h,INFINITE);
2760 DEBUGF(1,("Done Waiting for pseudo worker (%d)", row));
2761 }
2762 }
2763 #endif
2764
2765 #endif /* WIN32 */
2766