]> git.ipfire.org Git - thirdparty/lldpd.git/blame - src/daemon/priv.c
interfaces: switch to Netlink to gather interface-related information
[thirdparty/lldpd.git] / src / daemon / priv.c
CommitLineData
4b292b55 1/* -*- mode: c; c-file-style: "openbsd" -*- */
b5562b23
VB
2/*
3 * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
4 *
51434125 5 * Permission to use, copy, modify, and/or distribute this software for any
b5562b23
VB
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18/* This file contains code for privilege separation. When an error arises in
19 * monitor (which is running as root), it just stops instead of trying to
20 * recover. This module also contains proxies to privileged operations. In this
21 * case, error can be non fatal. */
22
23#include "lldpd.h"
24
25#include <stdio.h>
26#include <unistd.h>
27#include <errno.h>
b5562b23
VB
28#include <sys/wait.h>
29#include <sys/stat.h>
d72a05d4
VB
30#include <sys/socket.h>
31#include <sys/un.h>
29e043b5 32#include <regex.h>
b5562b23 33#include <fcntl.h>
b5562b23
VB
34#include <grp.h>
35#include <sys/utsname.h>
4afe659e 36#include <sys/ioctl.h>
b5562b23 37#include <netdb.h>
e12c2365
VB
38#include <netpacket/packet.h>
39#include <net/ethernet.h>
d38eae28
VB
40
41/* Use resolv.h */
42#ifdef HAVE_SYS_TYPES_H
43# include <sys/types.h>
44#endif
45#ifdef HAVE_NETINET_IN_H
46# include <netinet/in.h>
47#endif
48#ifdef HAVE_ARPA_NAMESER_H
49# include <arpa/nameser.h> /* DNS HEADER struct */
50#endif
51#ifdef HAVE_NETDB_H
52# include <netdb.h>
53#endif
c933faf6 54#include <resolv.h>
4afe659e
VB
55
56enum {
d72a05d4 57 PRIV_PING,
4afe659e
VB
58 PRIV_DELETE_CTL_SOCKET,
59 PRIV_GET_HOSTNAME,
60 PRIV_OPEN,
61 PRIV_ETHTOOL,
a7502371
VB
62 PRIV_IFACE_INIT,
63 PRIV_IFACE_MULTICAST,
d72a05d4 64 PRIV_SNMP_SOCKET,
b5562b23
VB
65};
66
4afe659e
VB
67static int may_read(int, void *, size_t);
68static void must_read(int, void *, size_t);
ef76f920 69static void must_write(int, const void *, size_t);
b5562b23 70
8888d191
VB
71static int remote; /* Other side */
72static int monitored = -1; /* Child */
73static int sock = -1;
b5562b23
VB
74
75/* Proxies */
76
8888d191 77static void
d72a05d4
VB
78priv_ping()
79{
80 int cmd, rc;
81 cmd = PRIV_PING;
82 must_write(remote, &cmd, sizeof(int));
83 must_read(remote, &rc, sizeof(int));
6f8925be 84 log_debug("privsep", "monitor ready");
d72a05d4
VB
85}
86
b5562b23
VB
87/* Proxy for ctl_cleanup */
88void
89priv_ctl_cleanup()
90{
4afe659e
VB
91 int cmd, rc;
92 cmd = PRIV_DELETE_CTL_SOCKET;
93 must_write(remote, &cmd, sizeof(int));
94 must_read(remote, &rc, sizeof(int));
b5562b23
VB
95}
96
97/* Proxy for gethostbyname */
98char *
99priv_gethostbyname()
100{
4afe659e
VB
101 int cmd, rc;
102 static char *buf = NULL;
103 cmd = PRIV_GET_HOSTNAME;
104 must_write(remote, &cmd, sizeof(int));
105 must_read(remote, &rc, sizeof(int));
106 if ((buf = (char*)realloc(buf, rc+1)) == NULL)
6f8925be 107 fatal("privsep", NULL);
4afe659e
VB
108 must_read(remote, buf, rc+1);
109 return buf;
b5562b23
VB
110}
111
112/* Proxy for open */
113int
114priv_open(char *file)
115{
4afe659e
VB
116 int cmd, len, rc;
117 cmd = PRIV_OPEN;
118 must_write(remote, &cmd, sizeof(int));
119 len = strlen(file);
120 must_write(remote, &len, sizeof(int));
121 must_write(remote, file, len + 1);
122 must_read(remote, &rc, sizeof(int));
123 if (rc == -1)
124 return rc;
125 return receive_fd(remote);
126}
127
e12c2365 128#ifdef HOST_OS_LINUX
4afe659e
VB
129/* Proxy for ethtool ioctl */
130int
e12c2365 131priv_ethtool(char *ifname, void *ethc, size_t length)
4afe659e
VB
132{
133 int cmd, rc, len;
134 cmd = PRIV_ETHTOOL;
135 must_write(remote, &cmd, sizeof(int));
136 len = strlen(ifname);
137 must_write(remote, &len, sizeof(int));
138 must_write(remote, ifname, len + 1);
139 must_read(remote, &rc, sizeof(int));
140 if (rc != 0)
141 return rc;
e12c2365 142 must_read(remote, ethc, length);
4afe659e 143 return rc;
b5562b23 144}
e12c2365 145#endif
b5562b23 146
a7502371 147int
e12c2365 148priv_iface_init(int index)
a7502371
VB
149{
150 int cmd, rc;
151 cmd = PRIV_IFACE_INIT;
152 must_write(remote, &cmd, sizeof(int));
e12c2365 153 must_write(remote, &index, sizeof(int));
a7502371 154 must_read(remote, &rc, sizeof(int));
849954d7
VB
155 if (rc != 0) return -1;
156 return receive_fd(remote);
a7502371
VB
157}
158
159int
ef76f920 160priv_iface_multicast(const char *name, u_int8_t *mac, int add)
a7502371
VB
161{
162 int cmd, rc;
163 cmd = PRIV_IFACE_MULTICAST;
d72a05d4 164 must_write(remote, &cmd, sizeof(int));
a7502371
VB
165 must_write(remote, name, IFNAMSIZ);
166 must_write(remote, mac, ETH_ALEN);
167 must_write(remote, &add, sizeof(int));
168 must_read(remote, &rc, sizeof(int));
169 return rc;
170}
171
d72a05d4
VB
172int
173priv_snmp_socket(struct sockaddr_un *addr)
174{
175 int cmd, rc;
176 cmd = PRIV_SNMP_SOCKET;
177 must_write(remote, &cmd, sizeof(int));
178 must_write(remote, addr, sizeof(struct sockaddr_un));
179 must_read(remote, &rc, sizeof(int));
180 if (rc < 0)
181 return rc;
182 return receive_fd(remote);
183}
184
8888d191 185static void
d72a05d4
VB
186asroot_ping()
187{
188 int rc = 1;
189 must_write(remote, &rc, sizeof(int));
190}
191
8888d191 192static void
4afe659e 193asroot_ctl_cleanup()
b5562b23 194{
4afe659e 195 int rc = 0;
b5562b23 196 ctl_cleanup(LLDPD_CTL_SOCKET);
4afe659e
VB
197
198 /* Ack */
199 must_write(remote, &rc, sizeof(int));
b5562b23
VB
200}
201
8888d191 202static void
4afe659e 203asroot_gethostbyname()
b5562b23
VB
204{
205 struct utsname un;
206 struct hostent *hp;
4afe659e 207 int len;
b5562b23 208 if (uname(&un) != 0)
6f8925be 209 fatal("privsep", "failed to get system information");
b5a6c479 210 if ((hp = gethostbyname(un.nodename)) == NULL) {
6f8925be 211 log_info("privsep", "unable to get system name");
c933faf6 212 res_init();
b5a6c479
VB
213 len = strlen(un.nodename);
214 must_write(remote, &len, sizeof(int));
215 must_write(remote, un.nodename, len + 1);
216 } else {
217 len = strlen(hp->h_name);
218 must_write(remote, &len, sizeof(int));
219 must_write(remote, hp->h_name, len + 1);
220 }
b5562b23
VB
221}
222
8888d191 223static void
4afe659e 224asroot_open()
b5562b23 225{
4afe659e 226 const char* authorized[] = {
b5562b23 227 "/proc/sys/net/ipv4/ip_forward",
1d291522
VB
228 "/proc/net/bonding/[^.][^/]*",
229 "/proc/self/net/bonding/[^.][^/]*",
89840df0
VB
230 SYSFS_CLASS_NET "[^.][^/]*/brforward",
231 SYSFS_CLASS_NET "[^.][^/]*/brport",
bac91719 232 SYSFS_CLASS_NET "[^.][^/]*/brif/[^.][^/]*/port_no",
89840df0
VB
233 SYSFS_CLASS_DMI "product_version",
234 SYSFS_CLASS_DMI "product_serial",
235 SYSFS_CLASS_DMI "product_name",
236 SYSFS_CLASS_DMI "bios_version",
237 SYSFS_CLASS_DMI "sys_vendor",
238 SYSFS_CLASS_DMI "chassis_asset_tag",
b5562b23
VB
239 NULL
240 };
ef76f920 241 const char **f;
4afe659e
VB
242 char *file;
243 int fd, len, rc;
29e043b5 244 regex_t preg;
4afe659e
VB
245
246 must_read(remote, &len, sizeof(len));
247 if ((file = (char *)malloc(len + 1)) == NULL)
6f8925be 248 fatal("privsep", NULL);
29e043b5
VB
249 must_read(remote, file, len);
250 file[len] = '\0';
b5562b23
VB
251
252 for (f=authorized; *f != NULL; f++) {
29e043b5
VB
253 if (regcomp(&preg, *f, REG_NOSUB) != 0)
254 /* Should not happen */
6f8925be 255 fatal("privsep", "unable to compile a regex");
29e043b5
VB
256 if (regexec(&preg, file, 0, NULL, 0) == 0) {
257 regfree(&preg);
4afe659e 258 break;
29e043b5
VB
259 }
260 regfree(&preg);
b5562b23 261 }
4afe659e 262 if (*f == NULL) {
6f8925be 263 log_warnx("privsep", "not authorized to open %s", file);
4afe659e
VB
264 rc = -1;
265 must_write(remote, &rc, sizeof(int));
266 free(file);
b5562b23
VB
267 return;
268 }
0aa5f676 269 if ((fd = open(file, O_RDONLY)) == -1) {
4afe659e
VB
270 rc = -1;
271 must_write(remote, &rc, sizeof(int));
272 free(file);
b5562b23
VB
273 return;
274 }
4afe659e
VB
275 free(file);
276 must_write(remote, &fd, sizeof(int));
277 send_fd(remote, fd);
b5562b23
VB
278 close(fd);
279}
280
e12c2365
VB
281#ifdef HOST_OS_LINUX
282#include <linux/ethtool.h>
283#include <linux/sockios.h>
8888d191 284static void
4afe659e
VB
285asroot_ethtool()
286{
287 struct ifreq ifr;
288 struct ethtool_cmd ethc;
a7502371 289 int len, rc;
4afe659e
VB
290 char *ifname;
291
292 memset(&ifr, 0, sizeof(ifr));
293 memset(&ethc, 0, sizeof(ethc));
294 must_read(remote, &len, sizeof(int));
295 if ((ifname = (char*)malloc(len + 1)) == NULL)
6f8925be 296 fatal("privsep", NULL);
29e043b5
VB
297 must_read(remote, ifname, len);
298 ifname[len] = '\0';
4afe659e 299 strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
aabbb9ac 300 free(ifname);
4afe659e
VB
301 ifr.ifr_data = (caddr_t)&ethc;
302 ethc.cmd = ETHTOOL_GSET;
4afe659e 303 if ((rc = ioctl(sock, SIOCETHTOOL, &ifr)) != 0) {
4afe659e 304 must_write(remote, &rc, sizeof(int));
4afe659e
VB
305 return;
306 }
4afe659e
VB
307 must_write(remote, &rc, sizeof(int));
308 must_write(remote, &ethc, sizeof(struct ethtool_cmd));
309}
e12c2365 310#endif
4afe659e 311
8888d191 312static void
849954d7 313asroot_iface_init()
a7502371
VB
314{
315 struct sockaddr_ll sa;
5fb6f53b 316 int s, rc = 0;
e12c2365 317 int ifindex;
a7502371 318
e12c2365 319 must_read(remote, &ifindex, sizeof(ifindex));
a7502371
VB
320
321 /* Open listening socket to receive/send frames */
322 if ((s = socket(PF_PACKET, SOCK_RAW,
323 htons(ETH_P_ALL))) < 0) {
5fb6f53b
VB
324 rc = errno;
325 must_write(remote, &rc, sizeof(rc));
a7502371
VB
326 return;
327 }
328 memset(&sa, 0, sizeof(sa));
329 sa.sll_family = AF_PACKET;
330 sa.sll_protocol = 0;
e12c2365 331 sa.sll_ifindex = ifindex;
a7502371 332 if (bind(s, (struct sockaddr*)&sa, sizeof(sa)) < 0) {
5fb6f53b
VB
333 rc = errno;
334 must_write(remote, &rc, sizeof(rc));
a7502371
VB
335 close(s);
336 return;
337 }
5fb6f53b 338 must_write(remote, &rc, sizeof(rc));
a7502371
VB
339 send_fd(remote, s);
340 close(s);
341}
342
8888d191 343static void
a7502371
VB
344asroot_iface_multicast()
345{
346 int add, rc = 0;
347 struct ifreq ifr;
348 memset(&ifr, 0, sizeof(ifr));
349 must_read(remote, ifr.ifr_name, IFNAMSIZ);
29e043b5 350 ifr.ifr_name[IFNAMSIZ-1] = '\0';
a7502371
VB
351 must_read(remote, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
352 must_read(remote, &add, sizeof(int));
353
354 if (ioctl(sock, (add)?SIOCADDMULTI:SIOCDELMULTI,
5fb6f53b
VB
355 &ifr) < 0)
356 rc = errno;
509861fe 357
a7502371
VB
358 must_write(remote, &rc, sizeof(rc));
359}
360
d72a05d4
VB
361static void
362asroot_snmp_socket()
363{
364 int sock, rc;
365 static struct sockaddr_un *addr = NULL;
366 struct sockaddr_un bogus;
367
368 if (!addr) {
369 addr = (struct sockaddr_un *)malloc(sizeof(struct sockaddr_un));
370 must_read(remote, addr, sizeof(struct sockaddr_un));
371 } else
372 /* We have already been asked to connect to a socket. We will
373 * connect to the same socket. */
374 must_read(remote, &bogus, sizeof(struct sockaddr_un));
375 if (addr->sun_family != AF_UNIX)
6f8925be 376 fatal("privsep", "someone is trying to trick me");
d72a05d4
VB
377 addr->sun_path[sizeof(addr->sun_path)-1] = '\0';
378
379 if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
6f8925be 380 log_warn("privsep", "cannot open socket");
d72a05d4
VB
381 must_write(remote, &sock, sizeof(int));
382 return;
383 }
384 if ((rc = connect(sock, (struct sockaddr *) addr,
385 sizeof(struct sockaddr_un))) != 0) {
6f8925be 386 log_info("privsep", "cannot connect to %s: %s",
5ac76eba 387 addr->sun_path, strerror(errno));
d72a05d4
VB
388 close(sock);
389 rc = -1;
390 must_write(remote, &rc, sizeof(int));
391 return;
392 }
393 must_write(remote, &rc, sizeof(int));
394 send_fd(remote, sock);
e7c8287d 395 close(sock);
d72a05d4
VB
396}
397
b5562b23
VB
398struct dispatch_actions {
399 int msg;
4afe659e 400 void(*function)(void);
b5562b23
VB
401};
402
8888d191 403static struct dispatch_actions actions[] = {
d72a05d4 404 {PRIV_PING, asroot_ping},
4afe659e
VB
405 {PRIV_DELETE_CTL_SOCKET, asroot_ctl_cleanup},
406 {PRIV_GET_HOSTNAME, asroot_gethostbyname},
407 {PRIV_OPEN, asroot_open},
408 {PRIV_ETHTOOL, asroot_ethtool},
849954d7 409 {PRIV_IFACE_INIT, asroot_iface_init},
a7502371 410 {PRIV_IFACE_MULTICAST, asroot_iface_multicast},
d72a05d4 411 {PRIV_SNMP_SOCKET, asroot_snmp_socket},
4afe659e 412 {-1, NULL}
b5562b23
VB
413};
414
415/* Main loop, run as root */
8888d191 416static void
b5562b23
VB
417priv_loop()
418{
4afe659e 419 int cmd;
b5562b23
VB
420 struct dispatch_actions *a;
421
4afe659e 422 while (!may_read(remote, &cmd, sizeof(int))) {
b5562b23 423 for (a = actions; a->function != NULL; a++) {
4afe659e
VB
424 if (cmd == a->msg) {
425 a->function();
b5562b23
VB
426 break;
427 }
428 }
429 if (a->function == NULL)
6f8925be 430 fatal("privsep", "bogus message received");
b5562b23
VB
431 }
432 /* Should never be there */
433}
434
8888d191 435static void
b5562b23
VB
436priv_exit()
437{
438 int status;
439 int rc;
440 if ((rc = waitpid(monitored, &status, WNOHANG)) == 0) {
6f8925be 441 log_debug("privsep", "killing child");
b5562b23
VB
442 kill(monitored, SIGTERM);
443 }
444 if ((rc = waitpid(monitored, &status, WNOHANG)) == -1)
445 _exit(0);
6f8925be 446 log_debug("privsep", "waiting for child %d to terminate", monitored);
b5562b23
VB
447}
448
4afe659e
VB
449/* If priv parent gets a TERM or HUP, pass it through to child instead */
450static void
451sig_pass_to_chld(int sig)
b5562b23 452{
4afe659e
VB
453 int oerrno = errno;
454 if (monitored != -1)
455 kill(monitored, sig);
456 errno = oerrno;
457}
458
459/* if parent gets a SIGCHLD, it will exit */
460static void
461sig_chld(int sig)
462{
6f8925be 463 log_debug("privsep", "received signal %d, exiting", sig);
b5562b23
VB
464 priv_exit();
465}
466
467/* Initialization */
468void
2e91d1a1 469priv_init(char *chrootdir, int ctl, uid_t uid, gid_t gid)
b5562b23 470{
2e91d1a1 471
b5562b23 472 int pair[2];
4afe659e 473 gid_t gidset[1];
90149ee3 474 int status;
b5562b23
VB
475
476 /* Create socket pair */
4afe659e 477 if (socketpair(AF_LOCAL, SOCK_DGRAM, PF_UNSPEC, pair) < 0)
6f8925be 478 fatal("privsep", "unable to create socket pair for privilege separation");
b5562b23 479
b5562b23
VB
480 /* Spawn off monitor */
481 if ((monitored = fork()) < 0)
6f8925be 482 fatal("privsep", "unable to fork monitor");
b5562b23
VB
483 switch (monitored) {
484 case 0:
485 /* We are in the children, drop privileges */
396cfdfe 486 if (RUNNING_ON_VALGRIND)
6f8925be 487 log_warnx("privsep", "running on valgrind, keep privileges");
396cfdfe
VB
488 else {
489 if (chroot(chrootdir) == -1)
6f8925be 490 fatal("privsep", "unable to chroot");
396cfdfe 491 if (chdir("/") != 0)
6f8925be 492 fatal("privsep", "unable to chdir");
396cfdfe
VB
493 gidset[0] = gid;
494 if (setresgid(gid, gid, gid) == -1)
6f8925be 495 fatal("privsep", "setresgid() failed");
396cfdfe 496 if (setgroups(1, gidset) == -1)
6f8925be 497 fatal("privsep", "setgroups() failed");
396cfdfe 498 if (setresuid(uid, uid, uid) == -1)
6f8925be 499 fatal("privsep", "setresuid() failed");
396cfdfe 500 }
b5562b23
VB
501 remote = pair[0];
502 close(pair[1]);
d72a05d4 503 priv_ping();
b5562b23
VB
504 break;
505 default:
506 /* We are in the monitor */
2e91d1a1 507 if (ctl != -1) close(ctl);
b5562b23
VB
508 remote = pair[1];
509 close(pair[0]);
510 if (atexit(priv_exit) != 0)
6f8925be 511 fatal("privsep", "unable to set exit function");
a7502371 512 if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
6f8925be 513 fatal("privsep", "unable to get a socket");
a7502371 514 }
4afe659e
VB
515
516 signal(SIGALRM, sig_pass_to_chld);
517 signal(SIGTERM, sig_pass_to_chld);
518 signal(SIGHUP, sig_pass_to_chld);
519 signal(SIGINT, sig_pass_to_chld);
520 signal(SIGQUIT, sig_pass_to_chld);
521 signal(SIGCHLD, sig_chld);
90149ee3
VB
522 if (waitpid(monitored, &status, WNOHANG) != 0)
523 /* Child is already dead */
524 _exit(1);
b5562b23
VB
525 priv_loop();
526 exit(0);
527 }
b5562b23 528}
4afe659e
VB
529
530/* Stolen from sbin/pflogd/privsep.c from OpenBSD */
531/*
532 * Copyright (c) 2003 Can Erkin Acar
533 * Copyright (c) 2003 Anil Madhavapeddy <anil@recoil.org>
534 *
51434125 535 * Permission to use, copy, modify, and/or distribute this software for any
4afe659e
VB
536 * purpose with or without fee is hereby granted, provided that the above
537 * copyright notice and this permission notice appear in all copies.
538 *
539 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
540 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
541 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
542 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
543 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
544 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
545 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
546 */
547
548/* Read all data or return 1 for error. */
549static int
550may_read(int fd, void *buf, size_t n)
551{
552 char *s = buf;
553 ssize_t res, pos = 0;
554
555 while (n > pos) {
556 res = read(fd, s + pos, n - pos);
557 switch (res) {
558 case -1:
559 if (errno == EINTR || errno == EAGAIN)
560 continue;
561 case 0:
562 return (1);
563 default:
564 pos += res;
565 }
566 }
567 return (0);
568}
569
570/* Read data with the assertion that it all must come through, or
571 * else abort the process. Based on atomicio() from openssh. */
572static void
573must_read(int fd, void *buf, size_t n)
574{
575 char *s = buf;
576 ssize_t res, pos = 0;
577
578 while (n > pos) {
579 res = read(fd, s + pos, n - pos);
580 switch (res) {
581 case -1:
582 if (errno == EINTR || errno == EAGAIN)
583 continue;
584 case 0:
585 _exit(0);
586 default:
587 pos += res;
588 }
589 }
590}
591
592/* Write data with the assertion that it all has to be written, or
593 * else abort the process. Based on atomicio() from openssh. */
594static void
ef76f920 595must_write(int fd, const void *buf, size_t n)
4afe659e 596{
ef76f920 597 const char *s = buf;
4afe659e
VB
598 ssize_t res, pos = 0;
599
600 while (n > pos) {
601 res = write(fd, s + pos, n - pos);
602 switch (res) {
603 case -1:
604 if (errno == EINTR || errno == EAGAIN)
605 continue;
606 case 0:
607 _exit(0);
608 default:
609 pos += res;
610 }
611 }
612}