-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.c
3938 lines (3766 loc) · 103 KB
/
main.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* mkcmd generated user interface
* built by mkcmd version 8.17 Rel
*/
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/uio.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <time.h>
#include <assert.h>
#include <sysexits.h>
#include "machine.h"
#include "fdrights.h"
#include "mkdtemp.h"
#include <errno.h>
#include <sys/param.h>
#include <pwd.h>
/* from getopt.m */
/* from getopt_key.m */
/* from xclate.m */
#line 44 "xclate.m"
static char rcsid[] =
"$Id: xclate.m,v 2.64 2010/01/08 14:29:12 ksb Exp $";
static const char acProto[] =
"0.8"; /* the protocol version chat */
#if HAVE_SYS_PIPE_H && ! defined(FREEBSD)
#include <sys/pipe.h>
#endif
#if !defined(PIPE_SIZE)
#define PIPE_SIZE 16384
#endif
#if !defined(MAX_XID_LEN)
#define MAX_XID_LEN 64
#endif
#if !defined(MAX_PID_LEN)
#define MAX_PID_LEN 32
#endif
/* Broken pump of the master stdin -> widows we used to do, -I obviated this
* For everyones safety never enable this (it breaks sshw and sapply). -- ksb
*/
static char
acMySpace[] = "xclXXXXXX",
acDevNull[] = "/dev/null",
acEnvCmd[] = "XCLATE";
static FILE
*fpVerbose; /* -v output hook, std_control.m */
/* from std_help.m */
/* from std_version.m */
/* from util_divstack.m */
/* from util_divconnect.m */
#line 10 "util_divconnect.m"
void *(*divNextLevel)(void *pvThisLevel) = (void *(*)(void *))0;
int (*divConnHook)(int fd, void *pvLevel) = (int (*)(int, void *))0;
/* from util_errno.m */
#line 8 "util_errno.m"
extern int errno;
/* from util_sigret.m */
/* from util_ppm.m */
#line 11 "util_ppm.m"
#if !defined(UTIL_PPM_ONCE)
#define UTIL_PPM_ONCE 1
typedef struct PPMnode {
void *pvbase; /* present start */
unsigned int uimax; /* max we can hold now */
unsigned int uiwide; /* how wide is one element? */
unsigned int uiblob; /* how many elements do we grow by? */
} PPM_BUF;
extern PPM_BUF *util_ppm_init(PPM_BUF *, unsigned int, unsigned int);
extern void *util_ppm_size(PPM_BUF *, unsigned int);
extern void util_ppm_free(PPM_BUF *);
extern int util_ppm_print(PPM_BUF *, FILE *);
#endif
/* from util_tmp.m */
/* from util_pipefit.m */
/* from util_fsearch.m */
/* from hosttype.m */
/* from std_control.m */
/* from util_home.m */
/* from bytes.m */
/* from util_mult.m */
#line 22 "util_mult.m"
#if !defined(PM__TOKEN)
#define PM__TOKEN
typedef struct PMnode { /* postfix multiplier */
char ckey; /* postfix letter */
char *pctext; /* for the help message */
unsigned long umult; /* mean times this */
char *pcplural; /* nil or plural for pctext */
} POST_MULT;
#endif /* protection from multiple #includes */
#line 1 "getopt.mi"
/* $Id: getopt.mi,v 8.10 2000/05/31 13:16:24 ksb Exp $
*/
#if 1 || 0 || 0
/* IBMR2 (AIX in the real world) defines
* optind and optarg in <stdlib.h> and confuses the heck out
* of the C compiler. So we use those externs. I guess we will
* have to stop using the old names. -- ksb
*/
static int
u_optInd = 1; /* index into parent argv vector */
static char
*u_optarg; /* argument associated with option */
#endif /* only if we use them */
#if 1
static int
u_optopt; /* character checked for validity */
#endif
#line 1 "util_divstack.mi"
/* $Id: util_divstack.mi,v 1.6 2008/08/25 14:18:37 ksb Exp $
*/
extern void divVersion(FILE *fpOut);
extern unsigned divInit(const char *pcName, const char *pcSpec);
extern char *divCurrent(char *pcForce), *divSelect(), *divIndex(unsigned uLevel);
extern int divNumber(unsigned *puFound), divSearch(int (*pfi)(const char *pcName, const char *pcDirect, int fActive, void *pvData), void *pvData), divResults(const char *pcValue), divPush(const char *pcNew), divDetach(const char *pcNew, const char *pcTag);
#line 1 "util_divconnect.mi"
/* $Id: util_divconnect.mi,v 1.1 2008/08/25 14:16:40 ksb Exp $
*/
extern int divConnect(const char *pcSocket, int (*pfiIndir)(int fd, char *pcCookie, void *pvData, char *pcType), void *pvLevel);
#line 1 "util_sigret.mi"
/* $Id: util_sigret.mi,v 8.24 2001/10/17 22:24:26 ksb Exp $
*/
#if !defined(SIGRET_T)
#if defined(FREEBSD) || defined(SUN5) || defined(HPUX9) || defined(HPUX10)
#define SIGRET_T void
#else
#define SIGRET_T int
#endif
#endif
#if !defined(HAVE_STICKY_SIGNALS)
/* It looks like some Solaris 2.X revs have sticky signals, but I
* can't find a pattern.
*/
#if defined(SUN5)
#define HAVE_STICKY_SIGNALS 0
#else
#define HAVE_STICKY_SIGNALS (!(defined(HPUX)))
#endif
#endif
#line 1 "util_pipefit.mi"
/* $Id: util_pipefit.mi,v 1.1 2007/09/16 20:34:59 ksb Exp $
*/
static pid_t PipeFit(const char *pcExec, char **ppcArgv, char **ppcEnv, int iFd);
#line 1 "util_fsearch.mi"
/* $Id: util_fsearch.mi,v 8.10 1998/09/16 13:37:42 ksb Exp $
*/
extern char *util_fsearch();
#line 1 "util_home.mi"
/* $Id: util_home.mi,v 8.3 2004/12/15 22:20:18 ksb Exp $
*/
extern char *util_home();
#line 1 "bytes.mi"
/* $Id: bytes.mi,v 8.20 1998/11/29 01:17:36 ksb Exp $
* post mult byte (of course the k should be Ki, etc.) (ksb)
*/
#define PMB_BYTE { '\000', "byte", 1UL, (char *)0 }
#define PMB_WORD { 'w', "word", 2UL, (char *)0 }
#define PMB_LONG { 'l', "long word", 4UL, (char *)0 }
#define PMB_BLOCK { 'b', "block", 512UL, (char *)0 }
#define PMB_KB { 'k', "kilobyte", 1024UL, (char *)0 }
#define PMB_MB { 'm', "megabyte", 1048576UL, (char *)0 }
#define PMB_GB { 'g', "gigabyte", 1073741824UL, (char *)0 }
static POST_MULT aPMBytes[] = {
PMB_BYTE, PMB_WORD, PMB_LONG, PMB_BLOCK, PMB_KB, PMB_MB, PMB_GB
};
#line 1 "util_mult.mi"
/* forward for scaled conversion --ksb
* $Id: util_mult.mi,v 8.3 2004/12/15 22:20:18 ksb Exp $
*/
extern unsigned long pm_cvt(/* char *pcDo, POST_MULT *pPM, int iEntries */);
static unsigned long pm_expr();
char
*progname = "$Id$",
*au_terse[] = {
" [-DEInqQsvwY] [-depth] [-H hr] [-L cap] [-T title] [-u unix] [xid] [client]",
" -h",
" -V",
" -m [-AdnqQrsv] [-depth] [-H hr] [-i input] [-L cap] [-N notify] [-O output] [-T title] [-u unix] [-W widow] [utility]",
(char *)0
},
*u_help[] = {
"-depth select an outer diversion, depth steps away",
"A add startup banner to nofity stream, for sshw",
"d do not publish this level in the linked environment",
"D use the current working directory of the selected instance",
"E use the stderr of the selected instance",
"h print this help message",
"H hr add a horizontal rule to end each output stream",
"i input replace the original stdin for the utility process",
"I use the stdin of the selected instance",
"L cap cap on memory based output buffer",
"m manage output for descendant processes",
"utility a command to produce output",
"n do not execute commands, trace only",
"N notify message this file, process, or fd for each finished section (with each xid)",
"O output redirect collated output to this file, process, or fd",
"q allow quicker processing of empty clients",
"Q tell the enclosing persistent instance to finish",
"r report exit codes in the notify stream (as code,xid)",
"s squeeze out header+footer from tasks with no other output",
"T title a title for each stream, replace %x with xid, %s with sequence",
"u unix specify a forced unix domain end-point name",
"v be verbose",
"V show version information",
"w redirect our output to the widow section, not the collated section",
"W widow redirect widowed output to this file, process, or fd",
"Y change the controlling terminal to the new stdin, stdout, or stderr",
"xid the identification for this stream, usually xapply's %1",
(char *)0
},
*pcLimit = (char *)0,
*pcTmpdir = (char *)0;
int
iDevDepth = 0,
fReplaceOrigIn = 0,
fGaveL = 0,
cEsc = '%',
fStartup = 0,
fPublish = 1,
fAskPWD = 0,
fAskStderr = 0;
char
*pcHorizRule = (char *)0,
*pcForcedIn = (char *)0;
int
fAskStdin = 0;
long
uiLimit = 0L;
int
fMaster = 0,
fExec = 1;
char
*pcNotify = (char *)0,
*pcPriv = (char *)0;
int
wElected = 0,
fAllowQuick = 0,
fTerminate = 0,
fExit2 = 0,
fSqueeze = 0;
char
*pcInterp = (char *)0,
*pcTitle = (char *)0,
*pcMaster = (char *)0;
int
fVerbose = 0,
fWeeping = 0;
char
*pcWidows = (char *)0;
int
fNewTTY = 0,
Z_opt = 0;
#ifndef u_terse
#define u_terse (au_terse[0])
#endif
static void
u_nameof(pcBuf, ch)
char *pcBuf, ch;
{
if ('#' == ch) {
(void)strcpy(pcBuf, "`-depth\'");
} else {
(void)strcpy(pcBuf, "option `-K\'");
pcBuf[9] = ch;
}
}
static void
u_chkonly(chSlot, chOpt, pcList)
int chSlot, chOpt;
char *pcList;
{
register int chWas;
static int sbiOnly['w'-'#'+1];
auto char acOpt[12], acWas[12];
chWas = sbiOnly[chSlot-'#'];
u_nameof(acWas, chWas);
if (chOpt == chWas) {
fprintf(stderr, "%s: -%s cannot be given more than once\n", progname, acWas);
exit(1);
} else if (0 != chWas) {
u_nameof(acOpt, chOpt);
fprintf(stderr, "%s: %s forbidden by %s\n", progname, acOpt, acWas);
exit(1);
}
for (/*parameter*/; '\000' != *pcList; ++pcList) {
sbiOnly[*pcList-'#'] = chOpt;
}
}
/* convert text to control chars, we take `cat -v' style (ksb)
* [\]octal
* ^X (or ^x) contro-x
* M-x x plus 8th bit
* c a plain character
* return -1 -> no chars to convert
* return -2 -> unknown control code
* return -3 -> trailing junk on letter specification
*/
static int
cvtletter(pcScan)
char *pcScan;
{
register int cvt, n, i;
if ('\\' == pcScan[0]) {
switch (*++pcScan) {
case 'a':
cvt = '\007';
++pcScan;
break;
case '\n': /* why would this happen? */
case 'n': /* newline */
cvt = '\n';
++pcScan;
break;
case 't':
cvt = '\t';
++pcScan;
break;
case 'b':
cvt = '\b';
++pcScan;
break;
case 'r':
cvt = '\r';
++pcScan;
break;
case 'f':
cvt = '\f';
++pcScan;
break;
case 'v':
cvt = '\013';
++pcScan;
break;
case '\\':
++pcScan;
case '\000':
cvt = '\\';
break;
case '0': case '1': case '2': case '3':
case '4': case '5': case '6': case '7':
goto have_num;
case '8': case '9':
/* 8 & 9 are bogus octals,
* cc makes them literals
*/
/*fallthrough*/
default:
cvt = *pcScan++;
break;
}
} else if ('0' <= *pcScan && *pcScan <= '7' && '\000' != pcScan[1]) {
have_num:
cvt = *pcScan++ - '0';
for (i = 0; i < 2; i++) {
if (! isdigit(*pcScan)) {
break;
}
cvt <<= 3;
cvt += *pcScan++ - '0';
}
} else {
if ('M' == pcScan[0] && '-' == pcScan[1] && '\000' != pcScan[2]) {
cvt = 0x80;
pcScan += 2;
} else {
cvt = 0;
}
if ('\000' == *pcScan) {
return -1;
}
if ('^' == (n = *pcScan++) && '\000' != *pcScan) {
n = *pcScan++;
if (islower(n)) {
n = toupper(n);
}
if ('@' <= n && n <= '_') {
cvt |= n - '@';
} else if ('?' == n) {
cvt |= '\177';
} else {
return -2;
}
} else {
cvt |= n;
}
}
if ('\000' != *pcScan) {
return -3;
}
return cvt;
}
/* from getopt.m */
/* from getopt_key.m */
/* from xclate.m */
#line 243 "xclate.m"
/* Prevent the deadlock that version 2.8 had with widows (ksb)
*/
static void
WidowsWalk()
{
if (fMaster) {
return;
}
fprintf(stderr, "%s: recursive execution from the widow process leads to deadlock, becoming a cat\n", progname);
execlp("cat", "cat", "-", (char *)0);
execlp("/bin/cat", "cat", "-", (char *)0);
exit(EX_USAGE);
}
/* Find a safe place to make a /tmp (or $TMPDIR) directory (ksb)
*/
static char *
LocalTmp(const char *pcBase)
{
register char *pcMem;
register int iLen;
iLen = ((strlen(pcTmpdir)+1+strlen(pcBase)+2)|7)+1;
if ((char *)0 == (pcMem = malloc(iLen))) {
fprintf(stderr, "%s: malloc: %s\n", progname, strerror(errno));
exit(EX_OSERR);
}
snprintf(pcMem, iLen, "%s/%s/", pcTmpdir, pcBase);
return pcMem;
}
struct _WXnode { /* type for the pvBlob we expect */
int fsawactive;
const char *pcinfo;
const char *pcdirect;
};
/* Show some version information for a socket in the stack (ksb)
*/
static int
DiVersion(const char *pcId, const char *pcTag, int fActive, void *pvBlob)
{
register int fdCheck;
register struct _WXnode *pWX;
register char *pcTail;
auto struct stat stSock;
auto char acVersion[1024]; /* realy about 6 chars */
if ((const char *)0 == pcId) {
pcId = "--";
}
/* When wrapw is playing tricks we see her socket as if it
* were a directory, don't paninc and nobody gets hurt.
*/
stSock.st_mode = 0;
if ((char *)0 != (pcTail = strrchr(pcTag, '/'))) {
*pcTail = '\000';
(void)lstat(pcTag, & stSock);
*pcTail = '/';
}
if ((const char *)0 == pcTag) {
printf("%s: %2s: missing from our environment", progname, pcId);
} else if (S_IFSOCK != (S_IFMT & stSock.st_mode) && -1 == lstat(pcTag, &stSock)) {
printf("%s: %2s: stat: %s: %s", progname, pcId, pcTag, strerror(errno));
} else if (!fVerbose) {
printf("%s: %2s %s", progname, pcId, pcTag);
} else if (-1 == (fdCheck = divConnect(pcTag, RightsWrap, (void *)0)) || 0 >= read(fdCheck, acVersion, sizeof(acVersion)-1)) {
printf("%s: %2s %s: unresponsive", progname, pcId, pcTag);
} else {
close(fdCheck);
printf("%s: %2s %s: version %s", progname, pcId, pcTag, acVersion);
}
if ((struct _WXnode *)0 != (pWX = pvBlob)) {
if (fActive) {
pWX->fsawactive = 1;
printf("%s", pWX->pcinfo);
}
if ((const char *)0 != pcTag && (const char *)0 != pWX->pcdirect && 0 == strcmp(pWX->pcdirect, pcTag)) {
pWX->pcdirect = (const char *)0;
printf(" [-u]");
}
}
printf("\n");
if (fActive && !isdigit(*pcId)) {
return 1;
}
return '0' == pcId[0] && '\000' == pcId[1];
}
/* output the standard ksb version details (ksb)
*/
static void
Version()
{
auto unsigned uOuter;
auto struct _WXnode WXThis;
(void)divNumber(&uOuter);
divVersion(stdout);
printf("%s: protocol version %s\n", progname, acProto);
printf("%s: safe directory template: %s\n", progname, acMySpace);
WXThis.pcinfo = " [target]";
WXThis.fsawactive = 0;
WXThis.pcdirect = pcMaster;
if (0 == divSearch(DiVersion, (void *)&WXThis) && iDevDepth >= uOuter) {
if ((const char *)0 != WXThis.pcdirect) {
DiVersion("-u", WXThis.pcdirect, !WXThis.fsawactive, (void *)&WXThis);
} else if (0 == iDevDepth) {
printf("%s: no current diversions\n", progname);
} else {
printf("%s: depth -%d is too great for current stack of %d\n", progname, iDevDepth, uOuter);
}
} else if ((const char *)0 != WXThis.pcdirect) {
DiVersion("-u", WXThis.pcdirect, !WXThis.fsawactive, (void *)&WXThis);
} else if (!WXThis.fsawactive) {
printf("%s: never saw the active diversion\n", progname);
}
}
/* Make the server port for this server to listen on (ksb)
* stolen from ptyd
*/
static int
MakeServerPort(pcName)
char *pcName;
{
register int sCtl, iListen, iRet;
auto struct sockaddr_un run;
if ((sCtl = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
fprintf(stderr, "%s: socket: %s\n", progname, strerror(errno));
exit(EX_OSERR);
}
run.sun_family = AF_UNIX;
(void) strncpy(run.sun_path, pcName, sizeof(run.sun_path));
if (-1 == bind(sCtl, (struct sockaddr *)&run, EMU_SOCKUN_LEN(pcName))) {
return -1;
}
iRet = -1;
for (iListen = 20*64; iListen > 5 && -1 == (iRet = listen(sCtl, iListen)) ; iListen >>= 1) {
/* try less */
}
if (-1 == iRet) {
fprintf(stderr, "%s: listen: %d: %s\n", progname, iListen, strerror(errno));
exit(EX_OSERR);
}
return sCtl;
}
/* The sort for xid's is strange in that we look for a "number" in (ksb)
* the string to compare, as well as a lexical key.
* app10 <=> app2 (return -1)
* 100 <=> 101 (return 1)
* 10a <=> 10b (return 1)
* foo <=> foo1 (return 1)
* N.B. the strings cannot be (const char), sigh.
*/
static int
XidCmp(pcLeft, pcRight)
char *pcLeft, *pcRight;
{
register char *pcLNum, *pcRNum;
auto char *pcLRem, *pcRRem;
register int iRet;
for (pcLNum = pcLeft; '\000' != *pcLNum; ++pcLNum) {
if (isdigit(*pcLNum))
break;
}
for (pcRNum = pcRight; '\000' != *pcRNum; ++pcRNum) {
if (isdigit(*pcRNum))
break;
}
iRet = (pcRNum - pcRight) - (pcLNum - pcLeft);
if (0 != iRet) {
return iRet;
}
iRet = strncmp(pcLeft, pcRight, pcRNum-pcRight);
if (0 != iRet) {
return iRet;
}
pcLRem = pcRRem = "";
if (isdigit(*pcRNum)) {
if (isdigit(*pcLNum)) {
iRet = strtol(pcRight, &pcRRem, 10);
iRet -= strtol(pcLeft, &pcLRem, 10);
} else {
return 1;
}
} else if (isdigit(*pcLNum)) {
return -1;
} else {
iRet = 0;
}
if (0 != iRet) {
return iRet;
}
return strcmp(pcLRem, pcRRem);
}
/* Repeatedly call write(2) to flush large data out a small pipe (ksb)
* most modern unix systems don't need this, except HPUX
*/
ssize_t
ReWrite(int fd, const char *pc, size_t nb)
{
register ssize_t iRet, cc;
iRet = 0;
do {
cc = write(fd, pc, nb);
if (-1 == cc)
return cc;
if (0 == cc)
break;
pc += cc;
iRet += cc;
nb -= cc;
} while (nb > 0);
return iRet;
}
/* Let the mkcmd pipefit utility show our details better (ksb)
*/
static pid_t
CredPipeFit(const char *pcCred, const char *pcInterp, char **ppcFilter, char **ppcEnviron, int fdPush)
{
register pid_t wRet;
register size_t iLen;
auto char *pcSave;
register void *pvMem;
pcSave = progname;
pvMem = (void *)0;
if ((char *)0 != pcCred) {
iLen = (3|(strlen(progname)+2+strlen(pcCred)))+1;
if ((char *)0 == (progname = pvMem = malloc(iLen))) {
progname = pcSave;
} else {
snprintf(progname, iLen, "%s: %s", pcSave, pcCred);
}
}
wRet = PipeFit(pcInterp, ppcFilter, ppcEnviron, fdPush);
progname = pcSave;
if ((void *)0 != pvMem) {
free(pvMem);
}
return wRet;
}
#line 495 "xclate.m"
/* use mkcmd's path search function to find programs for PipeFit (ksb)
* unless the program is spec'd as "/full/path" or "./prog" or "../prog"
*/
static char *
FindBin(char *pcProg)
{
static char *apcList[2];
register char *pcRet;
if ('/' == pcProg[0] || ('.' == pcProg[0] && ('/' == pcProg[1] || ('.' == pcProg[1] && '/' == pcProg[2])))) {
return pcProg;
}
apcList[1] = (char *)0;
if ((char *)0 == (apcList[0] = pcProg)) {
apcList[0] = pcInterp;
}
if ((char *)0 == apcList[0]) {
apcList[0] = "/bin/sh";
}
if ((char *)0 != (pcRet = util_fsearch(apcList, getenv("PATH")))) {
return pcRet;
}
fprintf(stderr, "%s: %s: not found in PATH\n", progname, apcList[0]);
exit(EX_NOINPUT);
}
/* A file descriptor information entry for my use (ksb)
* N.B. now a bit set
*/
#define ACT_DEAD 0x0100 /* free slot */
#define ACT_UP 0x0200 /* special in,out,accept,widow,err*/
#define ACT_XID 0x0400 /* open, no xid yet */
#define ACT_QUICK 0x0800 /* done -> quick buffer */
#define ACT_OPEN 0x0000 /* open, no command pending */
#define ACT_INPUT 0x0001 /* asking for stdin */
#define ACT_OUTPUT 0x0002 /* asking for stdout */
#define ACT_ERROR 0x0004 /* asking for stderr */
#define ACT_WIDOW 0x0008 /* asking for widows (anyone) */
#define ACT_EXEC 0x0010 /* wants to fork unmanaged kid */
#define ACT_PEND 0x0020 /* claims to have output */
#define ACT_BMAX 0x0040 /* claims a full output buffer */
#define ACT_DONE 0x0080 /* claims quick, ready to flush */
#define ACT_MASK_PRIO (ACT_EXEC|ACT_PEND|ACT_BMAX|ACT_DONE)
typedef struct PLMnode {
int factive; /* fd status from ACT_ above */
int icode; /* exit code from client */
char acxid[MAX_XID_LEN]; /* name of stream */
} PUMPER;
/* Find a client stream to output (ksb)
* (the King is dead, long live the King)
* We'll sort the xid's of all the eq eligables, we'll take a finished
* stream over an active, we'll take a data stream over a pending one.
*/
static int
Elect(pPLM, iCount)
PUMPER *pPLM;
int iCount;
{
register int i, iRet, fActive;
register char *pcFound;
fActive = 0;
pcFound = (char *)0;
iRet = -1;
for (i = 0; i < iCount; ++i, ++pPLM) {
if (0 == (ACT_OUTPUT & pPLM->factive)) {
continue;
}
if (0 == (ACT_MASK_PRIO & pPLM->factive)) {
continue;
}
if ((char *)0 == pcFound) {
/* found one */;
} else if ((ACT_MASK_PRIO&fActive) < (ACT_MASK_PRIO&pPLM->factive)) {
/* better stream status */;
} else if (XidCmp(pcFound, pPLM->acxid) < 0) {
/* better order */;
} else {
/* stick with the one you know */
continue;
}
/* take the new one */
iRet = i;
fActive = pPLM->factive;
pcFound = pPLM->acxid;
}
return 0 == fActive ? -1 : iRet;
}
static volatile pid_t wInferior = -1;
static volatile int wInfExits = EX_PROTOCOL;
static volatile int fInfOpts = WNOHANG|WUNTRACED;
#if defined(DEBUG)
static volatile int wPidCount = 0; /* only a gdb aid */
#endif
/* When a signal for a child arrives we burry the dead. (ksb)
* If he is "our boy", then forget he's alive.
* N.B. Don't set the signal action up until you've set wInferior!
*/
static void
Death(int _dummy) /*ARGSUSED*/
{
register pid_t wReap;
auto int wStatus;
while (0 < (wReap = wait3(& wStatus, fInfOpts, (struct rusage *)0))) {
if (WIFSTOPPED(wStatus)) {
(void)kill(wReap, SIGCONT);
continue;
}
if (-1 == wInferior || wInferior == wReap) {
if (wInferior == wReap)
wInferior = 0;
#if defined(DEBUG)
++wPidCount;
#endif
wInfExits = WEXITSTATUS(wStatus);
}
}
}
/* send an unsigned interger to the peer (ksb)
*/
static void
TossWord(int fd, size_t wWord, int cSep)
{
auto char acCvt[64];
snprintf(acCvt, sizeof(acCvt), "%ld%c", (long)wWord, cSep);
ReWrite(fd, acCvt, strlen(acCvt));
}
/* decode the word we just sent in TossWord (ksb)
* or "." for no value.
*/
static int
CatchWord(int fd, size_t *pwFound)
{
auto char *pcEnd, acDecode[64];
register int i;
for (i = 0; i < sizeof(acDecode); ++i) {
if (1 != read(fd, acDecode+i, 1)) {
fprintf(stderr, "%s: read: %d: %s\n", progname, fd, strerror(errno));
exit(EX_PROTOCOL);
}
if (isdigit(acDecode[i]))
continue;
if ('.' == acDecode[i]) {
return 0;
}
/* must be a separator like : or \n
*/
acDecode[i] = '\000';
break;
}
if (sizeof(acDecode) == i) {
fprintf(stderr, "%s: string length too long\n", progname);
exit(EX_PROTOCOL);
}
pcEnd = (char *)0;
*pwFound = strtol(acDecode, &pcEnd, 10);
if ((char *)0 != pcEnd && '\000' != *pcEnd) {
fprintf(stderr, "%s: %s: length conversion failed\n", progname, acDecode);
exit(EX_PROTOCOL);
}
return 1;
}
/* send a (potentially long) string over the diversion socket (ksb)
*/
static void
TossString(int fd, char *pcDatum)
{
register size_t uLen;
if ((char *)0 == pcDatum) {
ReWrite(fd, ".", 1);
return;
}
uLen = ((unsigned long)strlen(pcDatum))+1;
TossWord(fd, uLen, ':');
ReWrite(fd, pcDatum, uLen);
}
/* decode what TossString sent us
*/
static char *
CatchString(int fd)
{
auto char *pcRet;
auto size_t wSize;
if (0 == CatchWord(fd, &wSize)) {
return (char *)0;
}
if ((char *)0 == (pcRet = malloc(wSize))) {
fprintf(stderr, "%s: malloc: %s\n", progname, strerror(errno));
exit(EX_OSERR);
}
if (wSize == read(fd, pcRet, wSize)) {
return pcRet;
}
fprintf(stderr, "%s: out of sync with peer\n", progname);
exit(EX_SOFTWARE);
}
/* Tell any notification agent about this tasks exit status (ksb)
* (the iTty flag saves a lot of duplicate fcntl calls, that's all).
*/
static void
Notify(int fdTo, PUMPER *pPLM, int iTty)
{
register int iLen;
auto char acCvt[8];
/* XXX do 'and' and 'or' options here from the todo
*/
if (-1 == fdTo) {
return ;
}
iLen = strlen(pPLM->acxid);
if (iTty) {
pPLM->acxid[iLen] = '\n';
}
if (fExit2) {
snprintf(acCvt, sizeof(acCvt), "%d,", pPLM->icode);
ReWrite(fdTo, acCvt, strlen(acCvt));
}
ReWrite(fdTo, pPLM->acxid, iLen+1);
}
/* A client connects to our socket and give us an xid and some status (ksb)
* We send him back someplace to put his data, and a sequence number.
* We copies his stream there, then closes the data channel, and the
* control connection to tell us he's done.
* We can notify an unrelated process of the termination of a client,
* for hxmd's use mostly.
* while we still have an open connection (widow or client)
* select on the fd's we have active
* close any done clients (or the widow stream)
* look for new clients
* add control connection
* read commands from clients
* update status
* as needed elect a new output stream, input, and stderr
*/
static void
PumpLikeMad(fdSource, fdAccept, fdOut, fdWidows, fdNotify, fColonMode)
int fdSource, fdAccept, fdOut, fdWidows, fdNotify, fColonMode;
{
register int cc;
auto PUMPER aPLM[FD_SETSIZE];
auto int iLeaseOut, iServing, iMaxPoll;
auto char *pcQuick;
auto fd_set fdsTemp, fdsReading;
auto char acCounter[(MAX_XID_LEN|31)+1];
auto int iConnections, iTty, fdSend, fdDrain, fdQuick;
auto unsigned int uiCapture;
auto struct sigaction saWant;
auto struct timeval stStall;
/* Build a (huge) list of clients to buffer. We know that we
* need 2 fd's for each client, so allocated buffers to max_fd/2
* clients only (we know fdAccept takes 1 pair off as well).
*/
for (cc = 0; cc < FD_SETSIZE; ++cc) {
aPLM[cc].factive = ACT_DEAD;
}
fdDrain = 0;
aPLM[fdDrain].factive = ACT_UP;
(void)strcpy(aPLM[0].acxid, "drain");
aPLM[2].factive = ACT_UP;
(void)strcpy(aPLM[2].acxid, "err");
aPLM[fdWidows].factive = ACT_UP;
(void)strcpy(aPLM[fdWidows].acxid, "widows");
aPLM[fdOut].factive = ACT_UP;
(void)strcpy(aPLM[fdOut].acxid, "out");
aPLM[fdAccept].factive = ACT_UP;
(void)strcpy(aPLM[fdAccept].acxid, "accept");
if (-1 != fdNotify) {
aPLM[fdNotify].factive = ACT_UP;
(void)strcpy(aPLM[fdNotify].acxid, "notify");
iTty = isatty(fdNotify);
}
if (-1 != fdSource) {
aPLM[fdSource].factive = ACT_UP;
(void)strcpy(aPLM[fdSource].acxid, "in");
}
(void)memset((void *)&saWant, '\000', sizeof(saWant));
saWant.sa_handler = SIG_IGN;
saWant.sa_flags = SA_RESTART;
if (-1 == sigaction(SIGPIPE, &saWant, (struct sigaction *)0)) {
fprintf(stderr, "%s: sigaction: restart: %s\n", progname, strerror(errno));
exit(EX_OSERR);
}
(void)memset((void *)& saWant, '\000', sizeof(saWant));
saWant.sa_handler = (void *)Death;
#if HAVE_SIGACTION
saWant.sa_sigaction = (void *)Death;
#endif
saWant.sa_flags = SA_RESTART;
if (-1 == sigaction(SIGCHLD, & saWant, (struct sigaction *)0)) {
fprintf(stderr, "%s: sigaction: CHLD: %s\n", progname, strerror(errno));
exit(EX_OSERR);
}
Death('P');
uiLimit |= 1023;
++uiLimit;
while (512 < uiLimit && (char *)0 == (pcQuick = malloc(uiLimit))) {
uiLimit >>= 1;
}
if (512 > uiLimit) {
fprintf(stderr, "%s: malloc: %s\n", progname, strerror(errno));
exit(EX_OSERR);
}
uiCapture = 0;
fdQuick = iLeaseOut = -1;
iMaxPoll = fdAccept;
FD_ZERO(&fdsReading);
FD_SET(fdAccept, &fdsReading);
FD_SET(fdDrain, &fdsReading);
iServing = 1;
iConnections = 0;