2
* Copyright (c) 1997, 1998, 1999, 2000, 2001
3
* Inferno Nettverk A/S, Norway. All rights reserved.
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions
8
* 1. The above copyright notice, this list of conditions and the following
9
* disclaimer must appear in all copies of the software, derivative works
10
* or modified versions, and any portions thereof, aswell as in all
11
* supporting documentation.
12
* 2. All advertising materials mentioning features or use of this software
13
* must display the following acknowledgement:
14
* This product includes software developed by
15
* Inferno Nettverk A/S, Norway.
16
* 3. The name of the author may not be used to endorse or promote products
17
* derived from this software without specific prior written permission.
19
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
* Inferno Nettverk A/S requests users of this software to return to
32
* Software Distribution Coordinator or sdc@inet.no
33
* Inferno Nettverk A/S
39
* any improvements or extensions that they make and grant Inferno Nettverk A/S
40
* the rights to redistribute these changes.
46
static const char rcsid[] =
47
"$Id: sockd_child.c,v 1.134 2001/12/18 12:38:51 karls Exp $";
49
#define MOTHER 0 /* descriptor mother reads/writes on. */
50
#define CHILD 1 /* descriptor child reads/writes on. */
55
setchildtype __P((int type, struct sockd_child_t ***childv, int **childc,
56
void (**function)(struct sockd_mother_t *mother)));
58
* Sets "childv", "childc" and "function" to the correct value depending
64
findchild __P((pid_t pid, int childc, const struct sockd_child_t *childv));
66
* Finds the child with pid "pid" in the array "childv". Searching
67
* Elements in "childv" is given by "childc".
69
* On success: the index of the child in "childv".
76
static struct sockd_child_t *iochildv; /* all our iochildren */
79
static struct sockd_child_t *negchildv; /* all our negotiatorchildren */
82
static struct sockd_child_t *reqchildv; /* all our requestchildren */
86
struct sockd_child_t *
90
const char *function = "addchild()";
92
* It is better to reserve some descriptors for temporary use
93
* than to get errors when passing them and thus lose clients.
95
const int reserved = FDPASS_MAX /* max descriptors we pass. */
96
+ 1 /* need a descriptor for accept(). */
97
+ 2; /* for each new child. */
98
struct sockd_mother_t mother;
99
struct sockd_child_t **childv;
101
void (*childfunction)(struct sockd_mother_t *mother);
104
int pipev[] = { -1, -1 };
105
int ackpipev[] = { -1, -1 };
108
* XXX This is a expensive test which shouldn't be hard to optimize
109
* away. It only happens when we are running low on slots though,
110
* so assume it's "good enough" until I get the time to fix it.
112
if (freedescriptors(NULL) < reserved) {
118
/* create datapipe. */
119
if (socketpair(AF_LOCAL, SOCK_STREAM, 0, pipev) != 0) {
120
swarn("%s: socketpair(AF_LOCAL, SOCK_STREAM)", function);
125
if (pipe(ackpipev) != 0) {
126
swarn("%s: pipe()", function);
127
closev(pipev, ELEMENTS(pipev));
132
* Try to set socketbuffer and watermarks to a optimal size.
134
switch (type = setchildtype(type, &childv, &childc, &childfunction)) {
135
case CHILD_NEGOTIATE:
137
* A negotiator child receives only descriptors, so mothers
138
* send buffer can be small, and so can the child's receive buffer.
139
* The child sends a sockd_request_t struct back to mother, so
140
* mothers recv buffer has to be considerably bigger, as does
141
* childs send buffer.
144
/* negotiator shouldn't block on sending to mother. */
145
if ((flags = fcntl(pipev[CHILD], F_GETFL, 0)) == -1
146
|| fcntl(pipev[CHILD], F_SETFL, flags | O_NONBLOCK) == -1)
147
swarn("%s: fcntl()", function);
149
#if HAVE_SENDMSG_DEADLOCK
150
if ((mother.lock = socks_mklock(SOCKS_LOCKFILE)) == -1) {
151
swarn("%s: socks_mklock()", function);
152
closev(pipev, ELEMENTS(pipev));
153
closev(ackpipev, ELEMENTS(ackpipev));
156
#endif /* HAVE_SENDMSG_DEADLOCK */
158
optval = sizeof(struct sockd_request_t) * (SOCKD_NEGOTIATEMAX + 1);
159
if (setsockopt(pipev[MOTHER], SOL_SOCKET, SO_RCVBUF, &optval,
161
|| setsockopt(pipev[CHILD], SOL_SOCKET, SO_SNDBUF, &optval,
162
sizeof(optval)) != 0)
163
swarn("%s: setsockopt(SO_RCVBUF/SO_SNDBUF)", function);
166
optval = sizeof(struct sockd_request_t) * LOWATSKEW;
167
if (setsockopt(pipev[CHILD], SOL_SOCKET, SO_SNDLOWAT, &optval,
169
|| setsockopt(pipev[MOTHER], SOL_SOCKET, SO_RCVLOWAT, &optval,
170
sizeof(optval)) != 0)
171
swarn("%s: setsockopt(SO_SNDLOWAT/SO_RCVLOWAT)", function);
177
* A request child receives a sockd_request_t structure,
178
* it sends back a sockd_io_t structure.
181
#if HAVE_SENDMSG_DEADLOCK
182
mother.lock = -1; /* doesn't need lock. */
183
#endif /* HAVE_SENDMSG_DEADLOCK */
185
optval = sizeof(struct sockd_request_t) * (SOCKD_REQUESTMAX + 1);
186
if (setsockopt(pipev[MOTHER], SOL_SOCKET, SO_SNDBUF, &optval,
188
|| setsockopt(pipev[CHILD], SOL_SOCKET, SO_RCVBUF, &optval,
189
sizeof(optval)) != 0)
190
swarn("%s: setsockopt()", function);
192
optval = sizeof(struct sockd_io_t) * (SOCKD_REQUESTMAX + 1);
193
if (setsockopt(pipev[MOTHER], SOL_SOCKET, SO_RCVBUF, &optval,
195
|| setsockopt(pipev[CHILD], SOL_SOCKET, SO_SNDBUF, &optval,
196
sizeof(optval)) != 0)
197
swarn("%s: setsockopt()", function);
200
optval = sizeof(struct sockd_request_t) * LOWATSKEW;
201
if (setsockopt(pipev[CHILD], SOL_SOCKET, SO_RCVLOWAT, &optval,
203
|| setsockopt(pipev[MOTHER], SOL_SOCKET, SO_SNDLOWAT, &optval,
204
sizeof(optval)) != 0)
205
swarn("%s: setsockopt(SO_RCVLOWAT)", function);
207
optval = sizeof(struct sockd_io_t) * LOWATSKEW;
208
if (setsockopt(pipev[CHILD], SOL_SOCKET, SO_SNDLOWAT, &optval,
210
|| setsockopt(pipev[MOTHER], SOL_SOCKET, SO_RCVLOWAT, &optval,
211
sizeof(optval)) != 0)
212
swarn("%s: setsockopt(SO_RCVLOWAT/SO_SNDLOWAT)", function);
218
* A io child receives a sockd_io_t structure,
219
* it sends back only a ack.
222
#if HAVE_SENDMSG_DEADLOCK
223
mother.lock = -1; /* doesn't need lock. */
224
#endif /* HAVE_SENDMSG_DEADLOCK */
226
optval = sizeof(struct sockd_io_t) * (SOCKD_IOMAX + 1);
227
if (setsockopt(pipev[MOTHER], SOL_SOCKET, SO_SNDBUF, &optval,
229
|| setsockopt(pipev[CHILD], SOL_SOCKET, SO_RCVBUF, &optval,
230
sizeof(optval)) != 0)
231
swarn("%s: setsockopt(SO_SNDBUF/SO_RCVBUF)", function);
233
optval = sizeof(int) * (SOCKD_IOMAX + 1);
234
if (setsockopt(pipev[MOTHER], SOL_SOCKET, SO_RCVBUF, &optval,
236
|| setsockopt(pipev[CHILD], SOL_SOCKET, SO_SNDBUF, &optval,
237
sizeof(optval)) != 0)
238
swarn("%s: setsockopt(SO_RCVBUF/SO_SNDBUF)", function);
241
optval = sizeof(struct sockd_io_t) * LOWATSKEW;
242
if (setsockopt(pipev[CHILD], SOL_SOCKET, SO_RCVLOWAT, &optval,
244
|| setsockopt(pipev[MOTHER], SOL_SOCKET, SO_SNDLOWAT, &optval,
245
sizeof(optval)) != 0)
246
swarn("%s: setsockopt(SO_RCVLOWAT)", function);
254
switch ((pid = fork())) {
256
swarn("%s: fork()", function);
257
closev(pipev, ELEMENTS(pipev));
258
closev(ackpipev, ELEMENTS(ackpipev));
260
#if HAVE_SENDMSG_DEADLOCK
261
if (mother.lock != -1)
263
#endif /* HAVE_SENDMSG_DEADLOCK */
269
struct sigaction sigact;
271
sockscf.state.type = type;
273
sockscf.state.pid = getpid(); /* for logmessage. */
274
slog(LOG_INFO, "created new %schild", childtype2string(type));
276
slog(LOG_DEBUG, "sleeping...");
280
mother.s = pipev[CHILD];
281
mother.ack = ackpipev[CHILD];
284
* It would be nice to be able to lose all privileges here
285
* but unfortunately we can't, yet.
287
* negotiation children:
288
* could need privileges to check password.
291
* could need privileges to bind port.
294
* could need privileges to bind port if using redirect()
295
* module, also SIGHUP performs misc. seteuid() tests that
296
* could fail if we lose privileges.
300
case CHILD_NEGOTIATE:
302
#if SOCKD_NEGOTIATEMAX > 1
304
#endif /* SOCKD_NEGOTIATEMAX > 1 */
305
#endif /* HAVE_LIBWRAP */
310
#if SOCKD_REQUESTMAX > 1
312
#endif /* SOCKD_REQUESTMAX > 1 */
313
#endif /* HAVE_LIBWRAP */
320
#endif /* SOCKD_IOMAX > 1 */
321
#endif /* HAVE_LIBWRAP */
328
sigemptyset(&sigact.sa_mask);
331
/* signals mother has set up but which we ignore at this point. */
332
sigact.sa_handler = SIG_IGN;
334
#if HAVE_SIGNAL_SIGINFO
335
if (sigaction(SIGINFO, &sigact, NULL) != 0)
336
swarn("%s: sigaction(SIGINFO)", function);
337
#endif /* HAVE_SIGNAL_SIGINFO */
339
if (sigaction(SIGUSR1, &sigact, NULL) != 0)
340
swarn("%s: sigaction(USR1)", function);
342
/* delete everything we got from parent. */
343
for (i = 0, maxfd = getdtablesize(); i < maxfd; ++i) {
345
if (i == (size_t)mother.s
346
#if HAVE_SENDMSG_DEADLOCK
347
|| i == (size_t)mother.lock
348
#endif /* HAVE_SENDMSG_DEADLOCK */
349
|| i == (size_t)mother.ack)
352
if (descriptorisreserved(i))
359
childfunction(&mother);
364
struct sockd_child_t *newchildv;
366
if ((newchildv = (struct sockd_child_t *)realloc(*childv,
367
sizeof(**childv) * (*childc + 1))) == NULL) {
368
slog(LOG_WARNING, "%s: %s", function, NOMEM);
369
closev(pipev, ELEMENTS(pipev));
370
closev(ackpipev, ELEMENTS(ackpipev));
375
(*childv)[*childc].type = type;
376
(*childv)[*childc].pid = pid;
377
(*childv)[*childc].s = pipev[MOTHER];
378
#if HAVE_SENDMSG_DEADLOCK
379
(*childv)[*childc].lock = mother.lock;
380
#endif /* HAVE_SENDMSG_DEADLOCK */
381
(*childv)[*childc].ack = ackpipev[MOTHER];
384
close(ackpipev[CHILD]);
386
switch ((*childv)[*childc].type) {
387
case CHILD_NEGOTIATE:
388
(*childv)[*childc].freec = SOCKD_NEGOTIATEMAX;
392
(*childv)[*childc].freec = SOCKD_REQUESTMAX;
396
(*childv)[*childc].freec = SOCKD_IOMAX;
400
SERRX((*childv)[*childc].type);
403
return &(*childv)[(*childc)++];
414
struct sockd_child_t **childv;
418
case -CHILD_NEGOTIATE:
419
case CHILD_NEGOTIATE:
422
min = SOCKD_FREESLOTS;
423
max = SOCKD_NEGOTIATEMAX;
430
min = SOCKD_FREESLOTS;
431
max = SOCKD_REQUESTMAX;
438
/* attempt to keep in a state where we can accept all requests. */
439
min = MAX(SOCKD_FREESLOTS, childcheck(-CHILD_REQUEST));
448
* get a estimate over how many (new) clients our children are able to
451
for (child = 0, proxyc = 0; child < *childc; ++child) {
452
SASSERTX((*childv)[child].freec <= max);
453
proxyc += type < 0 ? max : (*childv)[child].freec;
457
if (proxyc < min && sockscf.state.addchild)
458
if (addchild(type) != NULL)
459
return childcheck(type);
461
sockscf.state.addchild = 0; /* don't retry until a child dies. */
470
const char *function = "fillset()";
475
* There is no point in setting data descriptor of child N unless
476
* child N+1 is able to accept the data from child N. So find
477
* out if we have slots of the various types available .
480
ioc = childcheck(CHILD_IO);
481
reqc = childcheck(CHILD_REQUEST);
482
negc = childcheck(CHILD_NEGOTIATE);
487
/* new clients we accept. */
489
for (i = 0; i < sockscf.internalc; ++i) {
490
SASSERTX(sockscf.internalv[i].s >= 0);
491
FD_SET(sockscf.internalv[i].s, set);
492
dbits = MAX(dbits, sockscf.internalv[i].s);
495
/* negotiator children. */
496
for (i = 0; i < negchildc; ++i) {
498
SASSERTX(negchildv[i].s >= 0);
499
FD_SET(negchildv[i].s, set);
500
dbits = MAX(dbits, negchildv[i].s);
503
/* we can always accept a ack ofcourse. */
504
SASSERTX(negchildv[i].ack >= 0);
505
FD_SET(negchildv[i].ack, set);
506
dbits = MAX(dbits, negchildv[i].ack);
509
/* request children. */
510
for (i = 0; i < reqchildc; ++i) {
512
SASSERTX(reqchildv[i].s >= 0);
513
FD_SET(reqchildv[i].s, set);
514
dbits = MAX(dbits, reqchildv[i].s);
517
/* we can always accept a ack ofcourse. */
518
SASSERTX(reqchildv[i].ack >= 0);
519
FD_SET(reqchildv[i].ack, set);
520
dbits = MAX(dbits, reqchildv[i].ack);
523
/* io children, last in chain. */
524
for (i = 0; i < iochildc; ++i) {
525
SASSERTX(iochildv[i].s >= 0);
526
FD_SET(iochildv[i].s, set);
527
dbits = MAX(dbits, iochildv[i].s);
529
SASSERTX(iochildv[i].ack >= 0);
530
FD_SET(iochildv[i].ack, set);
531
dbits = MAX(dbits, iochildv[i].ack);
538
clearset(type, child, set)
540
const struct sockd_child_t *child;
546
FD_CLR(child->ack, set);
549
case SOCKD_NEWREQUEST:
550
FD_CLR(child->s, set);
559
struct sockd_child_t *
566
/* check negotiator children for match. */
567
for (i = 0; i < negchildc; ++i)
569
case SOCKD_NEWREQUEST:
570
if (FD_ISSET(negchildv[i].s, set))
571
return &negchildv[i];
575
if (FD_ISSET(negchildv[i].ack, set))
576
return &negchildv[i];
580
/* check request children for match. */
581
for (i = 0; i < reqchildc; ++i)
583
case SOCKD_NEWREQUEST:
584
if (FD_ISSET(reqchildv[i].s, set))
585
return &reqchildv[i];
589
if (FD_ISSET(reqchildv[i].ack, set))
590
return &reqchildv[i];
594
/* check io children for match. */
595
for (i = 0; i < iochildc; ++i)
597
case SOCKD_NEWREQUEST:
598
if (FD_ISSET(iochildv[i].s, set))
603
if (FD_ISSET(iochildv[i].ack, set))
616
const char *function = "removechild()";
617
struct sockd_child_t **childv;
618
struct sockd_child_t *newchildv;
622
slog(LOG_DEBUG, "%s: %d", function, (int)pid);
624
setchildtype(childtype(pid), &childv, &childc, NULL);
626
child = findchild(pid, *childc, *childv);
627
SASSERTX(child >= 0);
629
close((*childv)[child].s);
630
close((*childv)[child].ack);
632
/* shift all following one down */
633
while (child < *childc - 1) {
634
(*childv)[child] = (*childv)[child + 1];
639
if ((newchildv = (struct sockd_child_t *)realloc(*childv,
640
sizeof(**childv) * (*childc + 1))) == NULL) {
641
slog(LOG_WARNING, NOMEM);
649
struct sockd_child_t *
653
const char *function = "nextchild()";
654
struct timeval timeout;
655
struct sockd_child_t **childv;
660
setchildtype(type, &childv, &childc, NULL);
663
for (i = 0, maxd = -1; i < *childc; ++i)
664
if ((*childv)[i].freec > 0) {
665
FD_SET((*childv)[i].s, &wset);
666
maxd = MAX(maxd, (*childv)[i].s);
676
switch (selectn(maxd, NULL, &wset, NULL, &timeout)) {
682
slog(LOG_DEBUG, "%s: no child writable", function);
686
return getset(SOCKD_NEWREQUEST, &wset);
691
setchildtype(type, childv, childc, function)
693
struct sockd_child_t ***childv;
695
void (**function)(struct sockd_mother_t *mother);
706
if (function != NULL)
711
case CHILD_NEGOTIATE:
713
*childv = &negchildv;
716
*childc = &negchildc;
718
if (function != NULL)
719
*function = &run_negotiate;
725
*childv = &reqchildv;
728
*childc = &reqchildc;
730
if (function != NULL)
731
*function = &run_request;
747
if (findchild(pid, iochildc, iochildv) != -1)
750
if (findchild(pid, negchildc, negchildv) != -1)
751
return CHILD_NEGOTIATE;
753
if (findchild(pid, reqchildc, reqchildv) != -1)
754
return CHILD_REQUEST;
756
if (pidismother(pid))
764
findchild(pid, childc, childv)
767
const struct sockd_child_t *childv;
771
for (i = 0; i < childc; ++i)
772
if (childv[i].pid == pid)
778
struct sockd_child_t *
784
struct sockd_child_t **childv;
786
switch (type = childtype(pid)) {
788
case CHILD_NEGOTIATE:
799
setchildtype(type, &childv, &childc, NULL);
801
if ((child = findchild(pid, *childc, *childv)) != -1)
802
return &(*childv)[child];
809
const struct sockd_io_t *io;
811
const char *function = "send_io()";
812
struct iovec iovec[1];
814
int w, fdsent, length;
815
CMSG_AALLOC(cmsg, sizeof(int) * FDPASS_MAX);
819
/* LINTED operands have incompatible pointer types */
820
iovec[0].iov_base = (const void *)io;
821
iovec[0].iov_len = sizeof(*io);
822
length += iovec[0].iov_len;
825
CMSG_ADDOBJECT(io->src.s, cmsg, sizeof(io->src.s) * fdsent++);
826
CMSG_ADDOBJECT(io->dst.s, cmsg, sizeof(io->dst.s) * fdsent++);
828
switch (io->state.command) {
830
case SOCKS_BINDREPLY:
831
if (!io->state.extension.bind)
833
/* else: */ /* FALLTHROUGH */
835
case SOCKS_UDPASSOCIATE:
836
CMSG_ADDOBJECT(io->control.s, cmsg, sizeof(io->control.s) * fdsent++);
843
SERRX(io->state.command);
847
msg.msg_iovlen = ELEMENTS(iovec);
851
CMSG_SETHDR_SEND(msg, cmsg, sizeof(int) * fdsent);
853
if ((w = sendmsg(s, &msg, 0)) != length) {
854
swarn("%s: sendmsg(): %d of %d", function, w, length);
867
send_client(s, client)
871
const char *function = "send_client()";
872
const char command = SOCKD_NEWREQUEST;
873
struct iovec iovec[1];
875
CMSG_AALLOC(cmsg, sizeof(int));
878
/* LINTED operands have incompatible pointer types */
879
iovec[0].iov_base = (const void *)&command;
880
iovec[0].iov_len = sizeof(command);
883
CMSG_ADDOBJECT(client, cmsg, sizeof(client) * fdsent++);
886
msg.msg_iovlen = ELEMENTS(iovec);
890
CMSG_SETHDR_SEND(msg, cmsg, sizeof(int) * fdsent);
892
if (sendmsg(s, &msg, 0) != sizeof(command)) {
893
swarn("%s: sendmsg()", function);
903
const struct sockd_request_t *req;
905
const char *function = "send_req()";
906
struct iovec iovec[1];
909
CMSG_AALLOC(cmsg, sizeof(int));
911
/* LINTED operands have incompatible pointer types */
912
iovec[0].iov_base = (const void *)req;
913
iovec[0].iov_len = sizeof(*req);
916
CMSG_ADDOBJECT(req->s, cmsg, sizeof(req->s) * fdsent++);
919
msg.msg_iovlen = ELEMENTS(iovec);
923
CMSG_SETHDR_SEND(msg, cmsg, sizeof(int) * fdsent);
925
if (sendmsg(s, &msg, 0) != sizeof(*req)) {
926
swarn("%s: sendmsg()", function);
934
sigchildbroadcast(sig, childtype)
940
if (childtype & CHILD_NEGOTIATE)
941
for (i = 0; i < negchildc; ++i)
942
kill(negchildv[i].pid, sig);
944
if (childtype & CHILD_REQUEST)
945
for (i = 0; i < reqchildc; ++i)
946
kill(reqchildv[i].pid, sig);
948
if (childtype & CHILD_IO)
949
for (i = 0; i < iochildc; ++i)
950
kill(iochildv[i].pid, sig);
956
const struct sockd_io_t *io;
959
const char *function = "printfd()";
960
struct sockaddr name;
962
char namestring[MAXSOCKADDRSTRING];
964
bzero(&name, sizeof(name));
965
namelen = sizeof(name);
966
/* LINTED pointer casts may be troublesome */
967
if (getsockname(io->src.s, &name, &namelen) != 0)
968
swarn("%s: getsockname(io->src)", function);
970
slog(LOG_DEBUG, "%s: io->src (%d), name: %s", prefix,
971
io->src.s, sockaddr2string(&name, namestring, sizeof(namestring)));
973
bzero(&name, sizeof(name));
974
namelen = sizeof(name);
975
/* LINTED pointer casts may be troublesome */
976
if (getsockname(io->dst.s, &name, &namelen) != 0)
977
swarn("%s: getsockname(io->dst)", function);
979
slog(LOG_DEBUG, "%s: io->dst (%d), name: %s", prefix, io->dst.s,
980
sockaddr2string(&name, namestring, sizeof(namestring)));
982
switch (io->state.command) {
984
case SOCKS_BINDREPLY:
985
if (!io->state.extension.bind)
987
/* else: */ /* FALLTHROUGH */
989
case SOCKS_UDPASSOCIATE:
990
bzero(&name, sizeof(name));
991
namelen = sizeof(name);
992
/* LINTED pointer casts may be troublesome */
993
if (getpeername(io->control.s, &name, &namelen)
995
swarn("%s: getpeername(io->control)", function);
998
slog(LOG_DEBUG, "%s: io->control (%d), name: <none>",
999
prefix, io->control.s);
1001
slog(LOG_DEBUG, "%s: io->control (%d), name: %s",
1002
prefix, io->control.s,
1003
sockaddr2string(&name, namestring, sizeof(namestring)));
1011
SERRX(io->state.command);