Changes to update Tomato RAF.
[tomato.git] / release / src / router / dnsmasq / src / helper.c
blobab691b7f516377569fe491cad4766914bb298606
1 /* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; version 2 dated June, 1991, or
6 (at your option) version 3 dated 29 June, 2007.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
13 You should have received a copy of the GNU General Public License
14 along with this program. If not, see <http://www.gnu.org/licenses/>.
17 #include "dnsmasq.h"
19 #ifdef HAVE_SCRIPT
21 /* This file has code to fork a helper process which recieves data via a pipe
22 shared with the main process and which is responsible for calling a script when
23 DHCP leases change.
25 The helper process is forked before the main process drops root, so it retains root
26 privs to pass on to the script. For this reason it tries to be paranoid about
27 data received from the main process, in case that has been compromised. We don't
28 want the helper to give an attacker root. In particular, the script to be run is
29 not settable via the pipe, once the fork has taken place it is not alterable by the
30 main process.
33 static void my_setenv(const char *name, const char *value, int *error);
34 static unsigned char *grab_extradata(unsigned char *buf, unsigned char *end, char *env, int *err);
36 #ifdef HAVE_LUASCRIPT
37 #define LUA_COMPAT_ALL
38 #include <lua.h>
39 #include <lualib.h>
40 #include <lauxlib.h>
42 #ifndef lua_open
43 #define lua_open() luaL_newstate()
44 #endif
46 lua_State *lua;
48 static unsigned char *grab_extradata_lua(unsigned char *buf, unsigned char *end, char *field);
49 #endif
52 struct script_data
54 int flags;
55 int action, hwaddr_len, hwaddr_type;
56 int clid_len, hostname_len, ed_len;
57 struct in_addr addr, giaddr;
58 unsigned int remaining_time;
59 #ifdef HAVE_BROKEN_RTC
60 unsigned int length;
61 #else
62 time_t expires;
63 #endif
64 unsigned char hwaddr[DHCP_CHADDR_MAX];
65 char interface[IF_NAMESIZE];
69 static struct script_data *buf = NULL;
70 static size_t bytes_in_buf = 0, buf_size = 0;
72 int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
74 pid_t pid;
75 int i, pipefd[2];
76 struct sigaction sigact;
78 /* create the pipe through which the main program sends us commands,
79 then fork our process. */
80 if (pipe(pipefd) == -1 || !fix_fd(pipefd[1]) || (pid = fork()) == -1)
82 send_event(err_fd, EVENT_PIPE_ERR, errno, NULL);
83 _exit(0);
86 if (pid != 0)
88 close(pipefd[0]); /* close reader side */
89 return pipefd[1];
92 /* ignore SIGTERM, so that we can clean up when the main process gets hit
93 and SIGALRM so that we can use sleep() */
94 sigact.sa_handler = SIG_IGN;
95 sigact.sa_flags = 0;
96 sigemptyset(&sigact.sa_mask);
97 sigaction(SIGTERM, &sigact, NULL);
98 sigaction(SIGALRM, &sigact, NULL);
100 if (!option_bool(OPT_DEBUG) && uid != 0)
102 gid_t dummy;
103 if (setgroups(0, &dummy) == -1 ||
104 setgid(gid) == -1 ||
105 setuid(uid) == -1)
107 if (option_bool(OPT_NO_FORK))
108 /* send error to daemon process if no-fork */
109 send_event(event_fd, EVENT_USER_ERR, errno, daemon->scriptuser);
110 else
112 /* kill daemon */
113 send_event(event_fd, EVENT_DIE, 0, NULL);
114 /* return error */
115 send_event(err_fd, EVENT_USER_ERR, errno, daemon->scriptuser);
117 _exit(0);
121 /* close all the sockets etc, we don't need them here.
122 Don't close err_fd, in case the lua-init fails.
123 Note that we have to do this before lua init
124 so we don't close any lua fds. */
125 for (max_fd--; max_fd >= 0; max_fd--)
126 if (max_fd != STDOUT_FILENO && max_fd != STDERR_FILENO &&
127 max_fd != STDIN_FILENO && max_fd != pipefd[0] &&
128 max_fd != event_fd && max_fd != err_fd)
129 close(max_fd);
131 #ifdef HAVE_LUASCRIPT
132 if (daemon->luascript)
134 const char *lua_err = NULL;
135 lua = lua_open();
136 luaL_openlibs(lua);
138 /* get Lua to load our script file */
139 if (luaL_dofile(lua, daemon->luascript) != 0)
140 lua_err = lua_tostring(lua, -1);
141 else
143 lua_getglobal(lua, "lease");
144 if (lua_type(lua, -1) != LUA_TFUNCTION)
145 lua_err = _("lease() function missing in Lua script");
148 if (lua_err)
150 if (option_bool(OPT_NO_FORK) || option_bool(OPT_DEBUG))
151 /* send error to daemon process if no-fork */
152 send_event(event_fd, EVENT_LUA_ERR, 0, (char *)lua_err);
153 else
155 /* kill daemon */
156 send_event(event_fd, EVENT_DIE, 0, NULL);
157 /* return error */
158 send_event(err_fd, EVENT_LUA_ERR, 0, (char *)lua_err);
160 _exit(0);
163 lua_pop(lua, 1); /* remove nil from stack */
164 lua_getglobal(lua, "init");
165 if (lua_type(lua, -1) == LUA_TFUNCTION)
166 lua_call(lua, 0, 0);
167 else
168 lua_pop(lua, 1); /* remove nil from stack */
170 #endif
172 /* All init done, close our copy of the error pipe, so that main process can return */
173 if (err_fd != -1)
174 close(err_fd);
176 /* loop here */
177 while(1)
179 struct script_data data;
180 char *p, *action_str, *hostname = NULL, *domain = NULL;
181 unsigned char *buf = (unsigned char *)daemon->namebuff;
182 unsigned char *end, *extradata, *alloc_buff = NULL;
183 int is6, err = 0;
185 free(alloc_buff);
187 /* we read zero bytes when pipe closed: this is our signal to exit */
188 if (!read_write(pipefd[0], (unsigned char *)&data, sizeof(data), 1))
190 #ifdef HAVE_LUASCRIPT
191 if (daemon->luascript)
193 lua_getglobal(lua, "shutdown");
194 if (lua_type(lua, -1) == LUA_TFUNCTION)
195 lua_call(lua, 0, 0);
197 #endif
198 _exit(0);
201 is6 = !!(data.flags & (LEASE_TA | LEASE_NA));
203 if (data.action == ACTION_DEL)
204 action_str = "del";
205 else if (data.action == ACTION_ADD)
206 action_str = "add";
207 else if (data.action == ACTION_OLD || data.action == ACTION_OLD_HOSTNAME)
208 action_str = "old";
209 else if (data.action == ACTION_TFTP)
211 action_str = "tftp";
212 is6 = (data.flags != AF_INET);
214 else
215 continue;
218 if (!is6)
220 /* stringify MAC into dhcp_buff */
221 p = daemon->dhcp_buff;
222 if (data.hwaddr_type != ARPHRD_ETHER || data.hwaddr_len == 0)
223 p += sprintf(p, "%.2x-", data.hwaddr_type);
224 for (i = 0; (i < data.hwaddr_len) && (i < DHCP_CHADDR_MAX); i++)
226 p += sprintf(p, "%.2x", data.hwaddr[i]);
227 if (i != data.hwaddr_len - 1)
228 p += sprintf(p, ":");
232 /* supplied data may just exceed normal buffer (unlikely) */
233 if ((data.hostname_len + data.ed_len + data.clid_len) > MAXDNAME &&
234 !(alloc_buff = buf = malloc(data.hostname_len + data.ed_len + data.clid_len)))
235 continue;
237 if (!read_write(pipefd[0], buf,
238 data.hostname_len + data.ed_len + data.clid_len, 1))
239 continue;
241 /* CLID into packet */
242 if (!is6)
243 for (p = daemon->packet, i = 0; i < data.clid_len; i++)
245 p += sprintf(p, "%.2x", buf[i]);
246 if (i != data.clid_len - 1)
247 p += sprintf(p, ":");
249 #ifdef HAVE_DHCP6
250 else
252 /* or IAID and server DUID for IPv6 */
253 sprintf(daemon->dhcp_buff3, "%s%u", data.flags & LEASE_TA ? "T" : "", data.hwaddr_type);
254 for (p = daemon->packet, i = 0; i < daemon->duid_len; i++)
256 p += sprintf(p, "%.2x", daemon->duid[i]);
257 if (i != daemon->duid_len - 1)
258 p += sprintf(p, ":");
261 /* duid not MAC for IPv6 */
262 for (p = daemon->dhcp_buff, i = 0; i < data.clid_len; i++)
264 p += sprintf(p, "%.2x", buf[i]);
265 if (i != data.clid_len - 1)
266 p += sprintf(p, ":");
269 #endif
271 buf += data.clid_len;
273 if (data.hostname_len != 0)
275 char *dot;
276 hostname = (char *)buf;
277 hostname[data.hostname_len - 1] = 0;
278 if (data.action != ACTION_TFTP)
280 if (!legal_hostname(hostname))
281 hostname = NULL;
282 else if ((dot = strchr(hostname, '.')))
284 domain = dot+1;
285 *dot = 0;
290 extradata = buf + data.hostname_len;
292 if (!is6)
293 inet_ntop(AF_INET, &data.addr, daemon->addrbuff, ADDRSTRLEN);
294 #ifdef HAVE_DHCP6
295 else
296 inet_ntop(AF_INET6, &data.hwaddr, daemon->addrbuff, ADDRSTRLEN);
297 #endif
299 /* file length */
300 if (data.action == ACTION_TFTP)
301 sprintf(daemon->dhcp_buff, "%u", data.hwaddr_len);
303 #ifdef HAVE_LUASCRIPT
304 if (daemon->luascript)
306 if (data.action == ACTION_TFTP)
308 lua_getglobal(lua, "tftp");
309 if (lua_type(lua, -1) != LUA_TFUNCTION)
310 lua_pop(lua, 1); /* tftp function optional */
311 else
313 lua_pushstring(lua, action_str); /* arg1 - action */
314 lua_newtable(lua); /* arg2 - data table */
315 lua_pushstring(lua, daemon->addrbuff);
316 lua_setfield(lua, -2, "destination_address");
317 lua_pushstring(lua, hostname);
318 lua_setfield(lua, -2, "file_name");
319 lua_pushstring(lua, daemon->dhcp_buff);
320 lua_setfield(lua, -2, "file_size");
321 lua_call(lua, 2, 0); /* pass 2 values, expect 0 */
324 else
326 lua_getglobal(lua, "lease"); /* function to call */
327 lua_pushstring(lua, action_str); /* arg1 - action */
328 lua_newtable(lua); /* arg2 - data table */
330 if (is6)
332 lua_pushstring(lua, daemon->dhcp_buff);
333 lua_setfield(lua, -2, "client_duid");
334 lua_pushstring(lua, daemon->packet);
335 lua_setfield(lua, -2, "server_duid");
336 lua_pushstring(lua, daemon->dhcp_buff3);
337 lua_setfield(lua, -2, "iaid");
340 if (!is6 && data.clid_len != 0)
342 lua_pushstring(lua, daemon->packet);
343 lua_setfield(lua, -2, "client_id");
346 if (strlen(data.interface) != 0)
348 lua_pushstring(lua, data.interface);
349 lua_setfield(lua, -2, "interface");
352 #ifdef HAVE_BROKEN_RTC
353 lua_pushnumber(lua, data.length);
354 lua_setfield(lua, -2, "lease_length");
355 #else
356 lua_pushnumber(lua, data.expires);
357 lua_setfield(lua, -2, "lease_expires");
358 #endif
360 if (hostname)
362 lua_pushstring(lua, hostname);
363 lua_setfield(lua, -2, "hostname");
366 if (domain)
368 lua_pushstring(lua, domain);
369 lua_setfield(lua, -2, "domain");
372 end = extradata + data.ed_len;
373 buf = extradata;
375 if (!is6)
376 buf = grab_extradata_lua(buf, end, "vendor_class");
377 #ifdef HAVE_DHCP6
378 else
379 for (i = 0; i < data.hwaddr_len; i++)
381 sprintf(daemon->dhcp_buff2, "vendor_class%i", i);
382 buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
384 #endif
386 buf = grab_extradata_lua(buf, end, "supplied_hostname");
388 if (!is6)
390 buf = grab_extradata_lua(buf, end, "cpewan_oui");
391 buf = grab_extradata_lua(buf, end, "cpewan_serial");
392 buf = grab_extradata_lua(buf, end, "cpewan_class");
393 buf = grab_extradata_lua(buf, end, "circuit_id");
394 buf = grab_extradata_lua(buf, end, "subscriber_id");
395 buf = grab_extradata_lua(buf, end, "remote_id");
398 buf = grab_extradata_lua(buf, end, "tags");
400 if (is6)
401 buf = grab_extradata_lua(buf, end, "relay_address");
402 else if (data.giaddr.s_addr != 0)
404 lua_pushstring(lua, inet_ntoa(data.giaddr));
405 lua_setfield(lua, -2, "relay_address");
408 for (i = 0; buf; i++)
410 sprintf(daemon->dhcp_buff2, "user_class%i", i);
411 buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
414 if (data.action != ACTION_DEL && data.remaining_time != 0)
416 lua_pushnumber(lua, data.remaining_time);
417 lua_setfield(lua, -2, "time_remaining");
420 if (data.action == ACTION_OLD_HOSTNAME && hostname)
422 lua_pushstring(lua, hostname);
423 lua_setfield(lua, -2, "old_hostname");
426 if (!is6)
428 lua_pushstring(lua, daemon->dhcp_buff);
429 lua_setfield(lua, -2, "mac_address");
432 lua_pushstring(lua, daemon->addrbuff);
433 lua_setfield(lua, -2, "ip_address");
435 lua_call(lua, 2, 0); /* pass 2 values, expect 0 */
438 #endif
440 /* no script, just lua */
441 if (!daemon->lease_change_command)
442 continue;
444 /* possible fork errors are all temporary resource problems */
445 while ((pid = fork()) == -1 && (errno == EAGAIN || errno == ENOMEM))
446 sleep(2);
448 if (pid == -1)
449 continue;
451 /* wait for child to complete */
452 if (pid != 0)
454 /* reap our children's children, if necessary */
455 while (1)
457 int status;
458 pid_t rc = wait(&status);
460 if (rc == pid)
462 /* On error send event back to main process for logging */
463 if (WIFSIGNALED(status))
464 send_event(event_fd, EVENT_KILLED, WTERMSIG(status), NULL);
465 else if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
466 send_event(event_fd, EVENT_EXITED, WEXITSTATUS(status), NULL);
467 break;
470 if (rc == -1 && errno != EINTR)
471 break;
474 continue;
477 if (data.action != ACTION_TFTP)
479 if (is6)
481 my_setenv("DNSMASQ_IAID", daemon->dhcp_buff3, &err);
482 my_setenv("DNSMASQ_SERVER_DUID", daemon->packet, &err);
485 if (!is6 && data.clid_len != 0)
486 my_setenv("DNSMASQ_CLIENT_ID", daemon->packet, &err);
488 if (strlen(data.interface) != 0)
489 my_setenv("DNSMASQ_INTERFACE", data.interface, &err);
491 #ifdef HAVE_BROKEN_RTC
492 sprintf(daemon->dhcp_buff2, "%u", data.length);
493 my_setenv("DNSMASQ_LEASE_LENGTH", daemon->dhcp_buff2, &err);
494 #else
495 sprintf(daemon->dhcp_buff2, "%lu", (unsigned long)data.expires);
496 my_setenv("DNSMASQ_LEASE_EXPIRES", daemon->dhcp_buff2, &err);
497 #endif
499 if (domain)
500 my_setenv("DNSMASQ_DOMAIN", domain, &err);
502 end = extradata + data.ed_len;
503 buf = extradata;
505 if (!is6)
506 buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS", &err);
507 #ifdef HAVE_DHCP6
508 else
510 if (data.hwaddr_len != 0)
512 buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS_ID", &err);
513 for (i = 0; i < data.hwaddr_len - 1; i++)
515 sprintf(daemon->dhcp_buff2, "DNSMASQ_VENDOR_CLASS%i", i);
516 buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err);
520 #endif
522 buf = grab_extradata(buf, end, "DNSMASQ_SUPPLIED_HOSTNAME", &err);
524 if (!is6)
526 buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_OUI", &err);
527 buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_SERIAL", &err);
528 buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_CLASS", &err);
529 buf = grab_extradata(buf, end, "DNSMASQ_CIRCUIT_ID", &err);
530 buf = grab_extradata(buf, end, "DNSMASQ_SUBSCRIBER_ID", &err);
531 buf = grab_extradata(buf, end, "DNSMASQ_REMOTE_ID", &err);
534 buf = grab_extradata(buf, end, "DNSMASQ_TAGS", &err);
536 if (is6)
537 buf = grab_extradata(buf, end, "DNSMASQ_RELAY_ADDRESS", &err);
538 else if (data.giaddr.s_addr != 0)
539 my_setenv("DNSMASQ_RELAY_ADDRESS", inet_ntoa(data.giaddr), &err);
541 for (i = 0; buf; i++)
543 sprintf(daemon->dhcp_buff2, "DNSMASQ_USER_CLASS%i", i);
544 buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err);
547 if (data.action != ACTION_DEL && data.remaining_time != 0)
549 sprintf(daemon->dhcp_buff2, "%u", data.remaining_time);
550 my_setenv("DNSMASQ_TIME_REMAINING", daemon->dhcp_buff2, &err);
553 if (data.action == ACTION_OLD_HOSTNAME && hostname)
555 my_setenv("DNSMASQ_OLD_HOSTNAME", hostname, &err);
556 hostname = NULL;
560 if (option_bool(OPT_LOG_OPTS))
561 my_setenv("DNSMASQ_LOG_DHCP", "1", &err);
563 /* we need to have the event_fd around if exec fails */
564 if ((i = fcntl(event_fd, F_GETFD)) != -1)
565 fcntl(event_fd, F_SETFD, i | FD_CLOEXEC);
566 close(pipefd[0]);
568 p = strrchr(daemon->lease_change_command, '/');
569 if (err == 0)
571 execl(daemon->lease_change_command,
572 p ? p+1 : daemon->lease_change_command,
573 action_str, daemon->dhcp_buff, daemon->addrbuff, hostname, (char*)NULL);
574 err = errno;
576 /* failed, send event so the main process logs the problem */
577 send_event(event_fd, EVENT_EXEC_ERR, err, NULL);
578 _exit(0);
582 static void my_setenv(const char *name, const char *value, int *error)
584 if (*error == 0 && setenv(name, value, 1) != 0)
585 *error = errno;
588 static unsigned char *grab_extradata(unsigned char *buf, unsigned char *end, char *env, int *err)
590 unsigned char *next;
592 if (!buf || (buf == end))
593 return NULL;
595 for (next = buf; *next != 0; next++)
596 if (next == end)
597 return NULL;
599 if (next != buf)
601 char *p;
602 /* No "=" in value */
603 if ((p = strchr((char *)buf, '=')))
604 *p = 0;
605 my_setenv(env, (char *)buf, err);
608 return next + 1;
611 #ifdef HAVE_LUASCRIPT
612 static unsigned char *grab_extradata_lua(unsigned char *buf, unsigned char *end, char *field)
614 unsigned char *next;
616 if (!buf || (buf == end))
617 return NULL;
619 for (next = buf; *next != 0; next++)
620 if (next == end)
621 return NULL;
623 if (next != buf)
625 lua_pushstring(lua, (char *)buf);
626 lua_setfield(lua, -2, field);
629 return next + 1;
631 #endif
633 static void buff_alloc(size_t size)
635 if (size > buf_size)
637 struct script_data *new;
639 /* start with reasonable size, will almost never need extending. */
640 if (size < sizeof(struct script_data) + 200)
641 size = sizeof(struct script_data) + 200;
643 if (!(new = whine_malloc(size)))
644 return;
645 if (buf)
646 free(buf);
647 buf = new;
648 buf_size = size;
652 /* pack up lease data into a buffer */
653 void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t now)
655 unsigned char *p;
656 unsigned int hostname_len = 0, clid_len = 0, ed_len = 0;
657 int fd = daemon->dhcpfd;
658 #ifdef HAVE_DHCP6
659 int is6 = !!(lease->flags & (LEASE_TA | LEASE_NA));
661 if (!daemon->dhcp)
662 fd = daemon->dhcp6fd;
663 #endif
665 /* no script */
666 if (daemon->helperfd == -1)
667 return;
669 if (lease->extradata)
670 ed_len = lease->extradata_len;
671 if (lease->clid)
672 clid_len = lease->clid_len;
673 if (hostname)
674 hostname_len = strlen(hostname) + 1;
676 buff_alloc(sizeof(struct script_data) + clid_len + ed_len + hostname_len);
678 buf->action = action;
679 buf->flags = lease->flags;
680 #ifdef HAVE_DHCP6
681 if (is6)
682 buf->hwaddr_len = lease->vendorclass_count;
683 else
684 #endif
685 buf->hwaddr_len = lease->hwaddr_len;
686 buf->hwaddr_type = lease->hwaddr_type;
687 buf->clid_len = clid_len;
688 buf->ed_len = ed_len;
689 buf->hostname_len = hostname_len;
690 buf->addr = lease->addr;
691 buf->giaddr = lease->giaddr;
692 memcpy(buf->hwaddr, lease->hwaddr, DHCP_CHADDR_MAX);
693 if (!indextoname(fd, lease->last_interface, buf->interface))
694 buf->interface[0] = 0;
696 #ifdef HAVE_BROKEN_RTC
697 buf->length = lease->length;
698 #else
699 buf->expires = lease->expires;
700 #endif
702 if (lease->expires != 0)
703 buf->remaining_time = (unsigned int)difftime(lease->expires, now);
704 else
705 buf->remaining_time = 0;
707 p = (unsigned char *)(buf+1);
708 if (clid_len != 0)
710 memcpy(p, lease->clid, clid_len);
711 p += clid_len;
713 if (hostname_len != 0)
715 memcpy(p, hostname, hostname_len);
716 p += hostname_len;
718 if (ed_len != 0)
720 memcpy(p, lease->extradata, ed_len);
721 p += ed_len;
723 bytes_in_buf = p - (unsigned char *)buf;
726 #ifdef HAVE_TFTP
727 /* This nastily re-uses DHCP-fields for TFTP stuff */
728 void queue_tftp(off_t file_len, char *filename, union mysockaddr *peer)
730 unsigned int filename_len;
732 /* no script */
733 if (daemon->helperfd == -1)
734 return;
736 filename_len = strlen(filename) + 1;
737 buff_alloc(sizeof(struct script_data) + filename_len);
738 memset(buf, 0, sizeof(struct script_data));
740 buf->action = ACTION_TFTP;
741 buf->hostname_len = filename_len;
742 buf->hwaddr_len = file_len;
744 if ((buf->flags = peer->sa.sa_family) == AF_INET)
745 buf->addr = peer->in.sin_addr;
746 #ifdef HAVE_IPV6
747 else
748 memcpy(buf->hwaddr, &peer->in6.sin6_addr, IN6ADDRSZ);
749 #endif
751 memcpy((unsigned char *)(buf+1), filename, filename_len);
753 bytes_in_buf = sizeof(struct script_data) + filename_len;
755 #endif
757 int helper_buf_empty(void)
759 return bytes_in_buf == 0;
762 void helper_write(void)
764 ssize_t rc;
766 if (bytes_in_buf == 0)
767 return;
769 if ((rc = write(daemon->helperfd, buf, bytes_in_buf)) != -1)
771 if (bytes_in_buf != (size_t)rc)
772 memmove(buf, buf + rc, bytes_in_buf - rc);
773 bytes_in_buf -= rc;
775 else
777 if (errno == EAGAIN || errno == EINTR)
778 return;
779 bytes_in_buf = 0;
783 #endif