Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/cmd/cs/cs.c
1808 views
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 <[email protected]> *
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
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
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
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
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
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
741