1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 1990-2011 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Eclipse Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
8 * *
9 * A copy of the License is available at *
10 * http://www.eclipse.org/org/documents/epl-v10.html *
11 * (with md5 checksum b35adb5213ca9657e911e9befb180842) *
12 * *
13 * Information and Software Systems Research *
14 * AT&T Research *
15 * Florham Park NJ *
16 * *
17 * Glenn Fowler <gsf@research.att.com> *
18 * *
19 ***********************************************************************/
20 #pragma prototyped
21 /*
22 * Glenn Fowler
23 * AT&T Research
24 *
25 * cs - connect stream control
26 */
27
28 static const char usage[] =
29 "[-?\n@(#)$Id: cs (AT&T Research) 2006-06-11 $\n]"
30 USAGE_LICENSE
31 "[+NAME?cs - connect stream control]"
32 "[+DESCRIPTION?\bcs\b displays, initiates, and terminates connect stream"
33 " services, and displays the contents of connect stream message files."
34 " If no options are spcified then the connect stream for \apath\a is"
35 " opened. If the corresponding service is not running then it is"
36 " initiated and the connection is attempted again. If \acommand\a is"
37 " specified then it is executed with the standard input, standard"
38 " output and standard error redirected to the \apath\a connect stream."
39 " If \acommand\a is omitted then the \b/dev/\b equivalent path for"
40 " the connect stream is listed on the standard output.]"
41
42 "[a:attribute?List the attribute \aname\a for each host. If \aname\a is \b-\b"
43 " then all attributes are listed. The hostname attribute is listed"
44 " without a label, all other attributes are listed as"
45 " \alabel\a=\avalue\a]:[name]"
46 "[c:cat?Catenate messages in the named \apath\a operands. If \apath\a is"
47 " omitted then the standard input is read.]"
48 "[d:debug?Set the debug trace level to \alevel\a. Higher levels produce"
49 " more output.]#[level]"
50 "[f:continuous?Used with \b--cat\b to list messages on a \apath\a or standard"
51 " input that is continuously updated.]"
52 "[h:hostenvironment?Lists \bHOSTNAME\b=\aname\a \bHOSTTYPE\b=\atype\a on the"
53 " standard output for the named \ahost\a operand or the local host if"
54 " \ahost\a is omitted. This is useful for \b.profile\b initialization.]"
55 "[i:interactive?Open an interactive connection to the connect stream. The"
56 " service is initiated if it is not already running.]"
57 "[k:kill?Send \asignal\a to the server on the connect stream named by"
58 " \apath\a. \asignal\a may be a signal name or signal number.]:[signal]"
59 "[l:list?List the active connect stream services on the standard output.]"
60 "[m:mount?List the active connect stream mount directories on the standard"
61 " output.]"
62 "[p:process?List the active connect stream process ids on the standard output.]"
63 "[q:query?Open an interactive connection to the connect stream if a service"
64 " is already running; fail otherwise.]"
65 "[r:raw?Raw mode \b--interactive\b connection.]"
66 "[s:iservice?List details for each active connect stream service on the"
67 " standard output. The output format is similar to an \bls\b(1)"
68 " \b--long\b listing, except the size field is the \btcp\b or \budp\b"
69 " port number, and the service base name appears as a symbolic link"
70 " to the network \b/proc\b path for the service process.]"
71 "[t:translate?\ahost\a name operands are translated to IP address dot notation"
72 " and listed on the standard output. If \ahost\a is omitted then the"
73 " standard input is read for host names, one per line.]"
74 "[C:call?Used with \b--cat\b to list only messages for the calls in"
75 " \acall-list\a.]:[call-list]"
76 "[O:open-flags?Set optional \bcsopen\b(3) flags. Used by the \bcs\b(3) library"
77 " to initiate remote connections.]:[flags]"
78 "[T:terse?Used with \b--cat\b to list terse messages for the calls in"
79 " \acall-list\a]:[call-list]"
80
81 "\n"
82 "\n[ [ - ] host | path [ command ... ] ]\n"
83 "\n"
84
85 "[+DATA?Static information for hosts in the local network is in the file"
86 " \b../share/lib/cs/local\b on \b$PATH\b. Each line in the \blocal\b"
87 " file provides information for a single host. The syntax is:"
88 " \ahost-name\a [ \aattribute\a=\avalue\a ... ]]. Attributes for the host"
89 " \blocal\b are inherited by all hosts. Locally administered attributes"
90 " may be added. \aattribute\a with predefined semantics are:]{"
91 " [+addr?The host IP address in dot notation.]"
92 " [+bias?The \bcoshell\b(1) multiplies the host load by \bbias\b"
93 " to prioritize host availability. \bbias\b > 1 makes"
94 " the host less likely to be chosen.]"
95 " [+busy?\bcoshell\b(1) jobs running on a host that has remained"
96 " busy for this amount of time are suspended until the"
97 " host returns to idle status.]"
98 " [+cpu?The number of cpus on the host as reported by"
99 " \bpackage\b(1).]"
100 " [+idle?The minimum interactive user idle time before"
101 " \bcoshell\b(1) will schedule a job on the host.]"
102 " [+pool?The \bcoshell\b(1) attempts to keep \bpool\b"
103 " host connections active.]"
104 " [+rating?The host rating as reported by \bpackage\b(1).]"
105 " [+type?The host type as reported by \bpackage\b(1).]"
106 "}"
107 "[+FILES]{"
108 " [+../share/lib/cs/local?Local host info list on \b$PATH\b.]"
109 " [+../share/lib/ss/\ahost\a?Host status files on \b$PATH\b.]"
110 "}"
111
112 "[+SEE ALSO?\bcoshell\b(1), \bcss\b(1), \bpackage\b(1), \bss\b(1), \bcs\b(3)]"
113 ;
114
115 #include <ast.h>
116 #include <coshell.h>
117 #include <cs.h>
118 #include <error.h>
119 #include <ftwalk.h>
120 #include <msg.h>
121 #include <proc.h>
122 #include <sig.h>
123 #include <tm.h>
124 #include <tok.h>
125 #include <debug.h>
126
127 #define LIST (1<<0)
128 #define LIST_MOUNT (1<<1)
129 #define LIST_PROCESS (1<<2)
130 #define LIST_SERVICE (1<<3)
131
132 #define MSG_LIST (MSG_LIST_USER<<0)
133 #define MSG_LIST_CONTINUOUS (MSG_LIST_USER<<1)
134
135 static struct
136 {
137 int list; /* list flags */
138 char* local; /* csname(0) */
139 } state;
140
141 /*
142 * host address translation
143 */
144
145 static void
address(const char * name)146 address(const char* name)
147 {
148 unsigned long addr;
149
150 if (addr = csaddr(name))
151 sfprintf(sfstdout, "name=%s addr=%s host=%s user=%s flags=:%s%s%s%s%s\n"
152 , csfull(addr)
153 , csntoa(addr)
154 , cs.host
155 , cs.user
156 , (cs.flags & CS_ADDR_LOCAL) ? "LOCAL:" : ""
157 , (cs.flags & CS_ADDR_NUMERIC) ? "NUMERIC:" : ""
158 , (cs.flags & CS_ADDR_SHARE) ? "SHARE:" : ""
159 , (cs.flags & CS_ADDR_REMOTE) ? "REMOTE:" : ""
160 , (cs.flags & (CS_ADDR_LOCAL|CS_ADDR_NUMERIC|CS_ADDR_SHARE|CS_ADDR_REMOTE)) ? "" : ":"
161 );
162 else
163 sfprintf(sfstdout, "addr=\n");
164 }
165
166 /*
167 * order by name
168 */
169
170 static int
order(register Ftw_t * f1,register Ftw_t * f2)171 order(register Ftw_t* f1, register Ftw_t* f2)
172 {
173 return f1->level == 3 ? strcoll(f1->name, f2->name) : 0;
174 }
175
176 /*
177 * list the service mount directories
178 */
179
180 #define PROC_OFF 4
181 #define SERVICE_COLS 37
182
183 static int
list(register Ftw_t * ftw)184 list(register Ftw_t* ftw)
185 {
186 register char* s;
187 register char* t;
188 register char* u;
189 register char* p;
190 char* port;
191 char* proc;
192 int mode;
193 int n;
194 uid_t uid;
195 struct stat st;
196
197 static char label[3][64];
198 static char qual_buf[64];
199 static char proc_buf[PATH_MAX + 1] = " -> ";
200 static char port_buf[PATH_MAX + 1];
201 static char serv_buf[PATH_MAX + 1];
202 static char time_buf[64];
203
204 static Sfio_t* sp;
205
206 if (ftw->level > 0)
207 {
208 if (ftw->level > elementsof(label))
209 {
210 ftw->status = FTW_SKIP;
211 mode = ftw->statb.st_mode & (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
212 if (strmatch(ftw->name, "*-*-*-*"))
213 {
214 s = qual_buf;
215 *s++ = '/';
216 t = strrchr(ftw->name, '-');
217 while (s < &qual_buf[sizeof(qual_buf) - 1] && (*s++ = *++t));
218 *s = 0;
219 }
220 else
221 qual_buf[0] = 0;
222 if (!streq(label[1], "share"))
223 mode |= (S_ISVTX|S_IXOTH);
224 if (!sp && !(sp = sfstropen()))
225 error(ERROR_SYSTEM|3, "out of space");
226 sfprintf(sp, "%s/X%s", ftw->path, CS_MNT_TAIL);
227 if (!(p = sfstruse(sp)))
228 error(ERROR_SYSTEM|3, "out of space");
229 s = p + ftw->pathlen + 1;
230 *s = CS_MNT_PROCESS;
231 if (pathgetlink(p, proc_buf + PROC_OFF, sizeof(proc_buf) - PROC_OFF) <= 0)
232 {
233 /*
234 * check for old single char cs mounts
235 */
236
237 *(s + 1) = 0;
238 if (pathgetlink(p, proc_buf + PROC_OFF, sizeof(proc_buf) - PROC_OFF) <= 0)
239 {
240 *s = CS_MNT_STREAM;
241 remove(p);
242 *(s + 1) = CS_MNT_TAIL[0];
243 remove(p);
244 return 0;
245 }
246 }
247 proc = proc_buf;
248 if (strncmp(proc + PROC_OFF, "/n/", 3))
249 u = proc + PROC_OFF;
250 else
251 {
252 t = proc + PROC_OFF + 3;
253 if (u = strchr(t, '/'))
254 {
255 *u = 0;
256 if (strcmp(t, state.local))
257 {
258 *u = '/';
259 u = 0;
260 }
261 else
262 *u = '/';
263 }
264 }
265 if (u && (n = strtol(u + 6, NiL, 10)) && kill(n, 0) && errno == ESRCH)
266 {
267 remove(p);
268 *s = CS_MNT_STREAM;
269 remove(p);
270 return 0;
271 }
272 if (state.list & LIST_SERVICE)
273 {
274 *s = CS_MNT_STREAM;
275 if (pathgetlink(p, port_buf, sizeof(port_buf)) > 0 && (port = strrchr(port_buf, '/')))
276 port++;
277 else
278 port = "";
279 if (stat(p, &st))
280 st.st_mtime = ftw->statb.st_mtime;
281 tmfmt(time_buf, sizeof(time_buf), "%?%QL", &st.st_mtime);
282 *s = CS_MNT_LOG;
283 if (stat(p, &st))
284 st = ftw->statb;
285 *(s - 1) = 0;
286 sfprintf(sfstdout, "%c%s 1 %-8s %-8s %7s %s %s%s%s\n", label[0][0], fmtmode(mode, 0) + 1, fmtuid(st.st_uid), (mode & S_IROTH) ? "other" : fmtgid(ftw->statb.st_gid), port, time_buf, label[2], qual_buf, proc);
287 }
288 else
289 {
290 n = sfprintf(sfstdout, "/dev/%s/%s/%s", label[0], label[1], label[2]);
291 if (!(mode & S_IROTH))
292 {
293 if (!(mode & S_IRGRP))
294 {
295 n += sfprintf(sfstdout, "/user");
296 if (ftw->statb.st_uid != geteuid())
297 n += sfprintf(sfstdout, "=%s", fmtuid(ftw->statb.st_uid));
298 }
299 else
300 {
301 n += sfprintf(sfstdout, "/group");
302 if (ftw->statb.st_gid != getegid())
303 n += sfprintf(sfstdout, "=%s", fmtgid(ftw->statb.st_gid));
304 }
305 }
306 if (*ftw->name == '-')
307 n += sfprintf(sfstdout, "/trust");
308 else
309 {
310 sfsprintf(port_buf, sizeof(port_buf) - 1, "%s/%s/%s/%s%s", CS_SVC_DIR, label[0], label[2], label[2], CS_SVC_SUFFIX);
311 uid = strtol(ftw->name, NiL, 0);
312 if (!pathpath(port_buf, "", PATH_ABSOLUTE|PATH_EXECUTE, serv_buf, sizeof(serv_buf)) || stat(serv_buf, &st) || st.st_uid != uid)
313 n += sfprintf(sfstdout, "/trust=%s", fmtuid(uid));
314 }
315 if (qual_buf[0])
316 n += sfprintf(sfstdout, "%s", qual_buf);
317 if (u && streq(label[1], "share"))
318 n += sfprintf(sfstdout, "/local");
319 if (*label[0] == 't')
320 {
321 *s = CS_MNT_AUTH;
322 if (access(p, F_OK))
323 n += sfprintf(sfstdout, "/other");
324 }
325 if (state.list &= ~LIST)
326 {
327 if ((state.list ^ (state.list>>1)) == (state.list | (state.list>>1)))
328 while (n++ < SERVICE_COLS)
329 sfputc(sfstdout, ' ');
330 if (state.list & LIST_PROCESS)
331 {
332 *(s - 1) = 0;
333 sfprintf(sfstdout, " %s", u ? u : proc + PROC_OFF);
334 }
335 if (state.list & LIST_MOUNT)
336 {
337 *(s - 1) = 0;
338 sfprintf(sfstdout, " %s", p);
339 }
340 }
341 sfprintf(sfstdout, "\n");
342 }
343 }
344 else
345 {
346 s = ftw->name;
347 if (ftw->level == 2 && streq(s, "share") && streq(s, state.local))
348 s = "local";
349 strncpy(label[ftw->level - 1], s, elementsof(label[0]) - 1);
350 }
351 }
352 return 0;
353 }
354
355 /*
356 * list messages in sp
357 * if continuous!=0 then act like tail -f
358 */
359
360 static int
msgcat(register Sfio_t * sp,register int flags,unsigned long call,unsigned long terse)361 msgcat(register Sfio_t* sp, register int flags, unsigned long call, unsigned long terse)
362 {
363 register long n;
364 Msg_call_t msg;
365
366 if (flags & MSG_LIST_CONTINUOUS)
367 sfset(sfstdout, SF_LINE, 1);
368 for (;;)
369 {
370 while ((n = msgrecv(sffileno(sp), &msg)) > 0)
371 if (MSG_MASK(msg.call) & call)
372 msglist(sfstdout, &msg, flags, terse);
373 if (n < 0)
374 return -1;
375 if (!(flags & MSG_LIST_CONTINUOUS))
376 return 0;
377 sleep(2);
378 }
379 }
380
381 int
main(int argc,char ** argv)382 main(int argc, char** argv)
383 {
384 char** ap;
385 int n;
386 int fd;
387 int hostenv = 0;
388 int initiate = CS_OPEN_READ;
389 int interactive = 0;
390 int msg = 0;
391 int clientflags = 0;
392 int remote = 0;
393 int translate = 0;
394 unsigned long call = ~0;
395 unsigned long terse = 0;
396 char* attr = 0;
397 char* host;
398 char* path;
399 char* proc;
400 char* sig = 0;
401 char* av[8];
402 Sfio_t* sp;
403 char buf[PATH_MAX + 1];
404 char tmp[PATH_MAX + 1];
405
406 NoP(argc);
407 setlocale(LC_ALL, "");
408 error_info.id = "cs";
409 debug(systrace(0));
410 for (;;)
411 {
412 switch (optget(argv, usage))
413 {
414 case 'a':
415 attr = opt_info.arg;
416 continue;
417 case 'c':
418 msg |= MSG_LIST;
419 continue;
420 case 'd':
421 error_info.trace = -opt_info.num;
422 continue;
423 case 'f':
424 msg |= MSG_LIST_CONTINUOUS;
425 continue;
426 case 'h':
427 hostenv = 1;
428 continue;
429 case 'r':
430 clientflags = CS_CLIENT_RAW;
431 /*FALLTHROUGH*/
432 case 'i':
433 interactive = 1;
434 msg |= MSG_LIST_ID;
435 continue;
436 case 'k':
437 sig = opt_info.arg;
438 continue;
439 case 'l':
440 state.list |= LIST;
441 continue;
442 case 'm':
443 state.list |= LIST_MOUNT;
444 continue;
445 case 'p':
446 state.list |= LIST_PROCESS;
447 continue;
448 case 'q':
449 interactive = 1;
450 initiate |= CS_OPEN_TEST;
451 continue;
452 case 's':
453 state.list |= LIST_SERVICE;
454 continue;
455 case 't':
456 translate = 1;
457 continue;
458 case 'C':
459 call = msgsetmask(opt_info.arg);
460 continue;
461 case 'T':
462 terse = msgsetmask(opt_info.arg);
463 continue;
464 case 'O':
465 remote = 1;
466 host = opt_info.arg;
467 for (;;)
468 {
469 switch (*host++)
470 {
471 case 0:
472 break;
473 case CS_REMOTE_OPEN_AGENT:
474 initiate |= CS_OPEN_AGENT;
475 continue;
476 case CS_REMOTE_OPEN_LOCAL:
477 initiate |= CS_OPEN_LOCAL;
478 continue;
479 case CS_REMOTE_OPEN_NOW:
480 initiate |= CS_OPEN_NOW;
481 continue;
482 case CS_REMOTE_OPEN_READ:
483 continue;
484 case CS_REMOTE_OPEN_SHARE:
485 initiate |= CS_OPEN_SHARE;
486 continue;
487 case CS_REMOTE_OPEN_TEST:
488 initiate |= CS_OPEN_TEST;
489 continue;
490 case CS_REMOTE_OPEN_TRUST:
491 initiate |= CS_OPEN_TRUST;
492 continue;
493 default:
494 error(2, "%c: unknown open flag", *(host - 1));
495 break;
496 }
497 break;
498 }
499 continue;
500 case '?':
501 error(ERROR_USAGE|4, "%s", opt_info.arg);
502 continue;
503 case ':':
504 error(2, "%s", opt_info.arg);
505 continue;
506 }
507 break;
508 }
509 argv += opt_info.index;
510 if (error_info.errors)
511 error(ERROR_USAGE|4, "%s", optusage(NiL));
512 if (msg & MSG_LIST)
513 {
514 if (!(path = *argv++))
515 sp = sfstdin;
516 else if (*argv)
517 error(ERROR_USAGE|4, "%s", optusage(NiL));
518 else if (!(sp = sfopen(NiL, path, "r")))
519 error(ERROR_SYSTEM|3, "%s: cannot read", path);
520 return msgcat(sp, msg, call, terse) != 0;
521 }
522 if (translate)
523 {
524 if (*argv)
525 while (host = *argv++)
526 address(host);
527 else
528 {
529 sfopen(sfstdin, NiL, "rt");
530 while (host = sfgetr(sfstdin, '\n', 1))
531 address(host);
532 }
533 return 0;
534 }
535 state.local = csname(0);
536 if ((path = *argv++) && path[0] == '-' && !path[1])
537 {
538 path = *argv++;
539 initiate |= CS_OPEN_TEST;
540 }
541 if (remote)
542 {
543 if (!path)
544 return 1;
545 if (initiate & CS_OPEN_AGENT)
546 {
547 register char* s = path;
548 register char* t = tmp;
549 register int n = 0;
550
551 /*
552 * get the unqualified-host connect stream path
553 */
554
555 while (*t++ = *s)
556 switch (*s++)
557 {
558 case '/':
559 while (*s == '/')
560 s++;
561 n++;
562 break;
563 case '.':
564 if (n == 3)
565 for (t--; *s && *s != '/'; s++);
566 break;
567 }
568 path = tmp;
569 }
570 if ((fd = csopen(path, initiate)) < 0)
571 return 1;
572 if (initiate & CS_OPEN_AGENT)
573 {
574 *cs.control = CS_MNT_AUTH;
575 remote = !access(cs.mount, F_OK);
576 sfprintf(sfstdout, "%s%s\n", cspath(fd, 0), remote ? ".A" : "");
577 if (remote)
578 {
579 close(fd);
580 sfsync(sfstdout);
581 if (csauth(-1, cs.mount, NiL))
582 return 1;
583 }
584 }
585 }
586 else if (sig)
587 {
588 if (path)
589 {
590 do
591 {
592 if ((fd = csopen(path, CS_OPEN_TEST)) < 0)
593 {
594 error(ERROR_SYSTEM|2, "%s: cannot open connect stream", path);
595 continue;
596 }
597 close(fd);
598 if (cs.flags & CS_ADDR_REMOTE)
599 host = cs.host;
600 else
601 {
602 *cs.control = CS_MNT_PROCESS;
603 if (pathgetlink(cs.mount, buf, sizeof(buf)) <= 0)
604 {
605 error(ERROR_SYSTEM|2, "%s: cannot get service process mount", path);
606 continue;
607 }
608 if (tokscan(buf, NiL, "/proc/%s", &proc) == 1)
609 host = 0;
610 else if (tokscan(buf, NiL, "/n/%s/proc/%s", &host, &proc) != 2)
611 {
612 error(2, "%s: %s: invalid service process mount", path, buf);
613 continue;
614 }
615 }
616 sfsprintf(tmp, sizeof(tmp), "-%s", sig);
617 ap = av;
618 if (host && !streq(host, state.local))
619 {
620 *ap++ = CS_REMOTE_SHELL;
621 *ap++ = host;
622 if (*cs.user)
623 {
624 *ap++ = "-l";
625 *ap++ = cs.user;
626 }
627 }
628 *ap++ = "kill";
629 *ap++ = tmp;
630 *ap++ = proc;
631 *ap = 0;
632 if (procclose(procopen(av[0], av, NiL, NiL, PROC_UID|PROC_GID|(*argv ? PROC_OVERLAY : 0))))
633 error(ERROR_SYSTEM|2, "%s: cannot %s %s to kill server", path, av[0], host);
634 } while (path = *argv++);
635 }
636 }
637 else if (state.list & LIST)
638 {
639 if (path)
640 error(1, "%s: argument not expected", path);
641 ap = av;
642 *ap++ = csvar(CS_VAR_LOCAL, 0);
643 if (pathpath(csvar(CS_VAR_SHARE, 0), "", PATH_EXECUTE, tmp, sizeof(tmp)))
644 *ap++ = tmp;
645 *ap = 0;
646 ftwalk((char*)av, list, FTW_MULTIPLE|FTW_PHYSICAL, order);
647 }
648 else if (attr)
649 {
650 if (!path)
651 {
652 while (proc = csattr(NiL, attr))
653 sfputr(sfstdout, proc, '\n');
654 }
655 else
656 {
657 n = *argv != 0;
658 hostenv = streq(attr, "-");
659 terse = streq(attr, "name");
660 do
661 {
662 if (proc = csattr(path, attr))
663 {
664 if (hostenv)
665 sfputr(sfstdout, "name", '=');
666 if (!terse && (hostenv || n))
667 sfputr(sfstdout, path, ' ');
668 sfputr(sfstdout, proc, '\n');
669 }
670 else if (streq(path, CS_HOST_SHARE) && (sp = csinfo(path, NiL)))
671 {
672 while (path = sfgetr(sp, '\n', 1))
673 if (proc = csattr(path, attr))
674 {
675 if (hostenv)
676 sfputr(sfstdout, "name", '=');
677 if (!terse)
678 sfputr(sfstdout, path, ' ');
679 sfputr(sfstdout, proc, '\n');
680 }
681 else
682 error(2, "%s: no host info", path);
683 sfclose(sp);
684 }
685 else
686 error(2, "%s: no host info", path);
687 } while (path = *argv++);
688 }
689 return 0;
690 }
691 else if (hostenv)
692 {
693 if (!path || streq(path, "local"))
694 path = state.local;
695 proc = csattr(path, "type");
696 sfprintf(sfstdout, "%s=%s %s=%s\n", CO_ENV_HOST, path, CO_ENV_TYPE, proc ? proc : "unknown");
697 return 0;
698 }
699 else if (path)
700 {
701 if ((fd = csopen(path, initiate)) < 0)
702 error(ERROR_SYSTEM|3, "%s: cannot open connect stream", path);
703 if (state.list & LIST_MOUNT)
704 {
705 if (*argv)
706 error(1, "%s: argument not expected", path);
707 if (cs.flags & CS_ADDR_REMOTE)
708 error(1, "%s: remote connect stream", path);
709 else
710 {
711 *(cs.control - 1) = 0;
712 sfputr(sfstdout, cs.mount, '\n');
713 }
714 }
715 else if (interactive)
716 return csclient(fd, path, "cs> ", NiL, clientflags) || error_info.errors;
717 else
718 {
719 if (*argv)
720 {
721 close(0);
722 close(1);
723 if (dup(fd) != 0 || dup(fd) != 1)
724 error(ERROR_SYSTEM|3, "%s: cannot redirect connect stream", path);
725 close(fd);
726 if (!(initiate & CS_OPEN_TEST) && csdaemon((1<<0)|(1<<1)))
727 error(ERROR_SYSTEM|3, "%s: cannot dive into background", path);
728 procopen(*argv, argv, NiL, NiL, PROC_OVERLAY|PROC_UID|PROC_GID);
729 error(ERROR_SYSTEM|4, "%s: %s: cannot execute", path, *argv);
730 }
731 sfprintf(sfstdout, "%s\n", cspath(fd, 0));
732 }
733 }
734 else if (interactive)
735 error(3, "connect stream argument expected");
736 else
737 sfprintf(sfstdout, "%s\n", cspath(0, 0));
738 return error_info.errors != 0;
739 }
740