Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/cmd/probe/probe.c
1808 views
1
/***********************************************************************
2
* *
3
* This software is part of the ast package *
4
* Copyright (c) 1989-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
* probe - generate/install/display language processor probe information
26
*
27
* three installation possibilities, from most inter-user sharing to least
28
*
29
* (1) $INSTALLROOT/lib/probe/probe set-uid to the owner of the
30
* probe hierarchy
31
* (2) probe hierarchy owned by the caller geteuid() (e.g., FAT fs)
32
* (3) otherwise info maintained per-user in $HOME/.probe/
33
*/
34
35
static const char usage[] =
36
"[-?\n@(#)$Id: probe (AT&T Research) 2007-02-22 $\n]"
37
USAGE_LICENSE
38
"[+NAME?probe - generate/install/display language processor probe information]"
39
"[+DESCRIPTION?\bprobe\b generates, installs and displays on the standard"
40
" output probe information for the \alanguage\a programming language"
41
" with respect to the \atool\a command as implemented by the"
42
" \aprocessor\a command. The probe information is in"
43
" the \atool\a command syntax. In general the commands that rely on"
44
" probe information (re)generate the information when it is out of date"
45
" (see \b--generate\b), so direct control is not usually"
46
" necessary. However, not all changes to \aprocessor\a that"
47
" would affect the probe information are detected by this mechanism;"
48
" such changes require manual intervention (see \b--delete\b and"
49
" \b--override\b.) A probe information file (see \b--key\b) with"
50
" write mode enabled signifies that manual changes have been made"
51
" and disables automatic regeneration (see \b--generate\b.)]"
52
"[+?\bprobe\b information is kept in a file with a pathname based on a"
53
" hash of \alanguage\a, \atool\a, and \aprocessor\a. The information"
54
" is generated by a \bsh\b(1) script (\aprobe script\a) specific to"
55
" each \alanguage-processor\a pair. Any options that change the"
56
" semantics of the language handled by \aprocessor\a should be included"
57
" in the \aprocessor\a operand. e.g., if \bcc -++\b processes C++ files,"
58
" then \aprocessor\a should be \bcc -++\b, not \bcc\b.]"
59
"[+?\bprobe\b generation may take a few minutes on some systems or for"
60
" some \aprocessors\a. Information is shared between users as much"
61
" as possible. If the \b--key\b option produces a path under your"
62
" \b$HOME\b directory then the probe installation does not support"
63
" sharing and each user will have private copies of the generated"
64
" information.]"
65
"[+?For some \alanguage\a/\atool\a combinations, the file"
66
" \blib/probe/\b\alanguage\a\b/\b\atool\a\b/probe.lcl\b, if it"
67
" exists in the same directory as the \bprobe\b script, is sourced"
68
" (via the \b.\b \bsh\b(1) command) before the probe variables are"
69
" emitted. The emitted values may be modified by assigning appropriate"
70
" \bsh\b variables (non-identifier name characters converted"
71
" to \b_\b.) Additional non-standard values may be written directly"
72
" to the standard output.]"
73
"[+?For all \alanguage\a/\atool\a combinations, the file"
74
" \blib/probe/\b\alanguage\a\b/\b\atool\a\b/probe.ini\b, if it"
75
" exists in the same directory as the \bprobe\b script, is sourced"
76
" (via the \b.\b \bsh\b(1) command) before the \bprobe\b script proper."
77
" \bprobe.ini\b may completely override the \bprobe\b script by"
78
" exiting (presumably after printing its own values on the"
79
" standard output.)]"
80
"[+?Sometimes it is necessary to maintain private \bprobe\b information"
81
" for some processors or tools. See \b--override\b for details.]"
82
83
"[a:attributes?List probe attribute definitions.]"
84
"[d:delete?Delete the current information. Information can only be deleted by"
85
" the generating user.]"
86
"[f:force?Force information generation even if it is out of date. Only probe"
87
" information files with write mode disabled can be regenerated.]"
88
"[g:generate?Generate the probe information if it is out of date. Only probe"
89
" information files with write mode disabled can be regenerated.]"
90
"[k:key?List the probe key path name on the standard output.]"
91
"[l:list?List the probe information on the standard output. An error occurs"
92
" if the information has not been generated, unless \b--generate\b"
93
" is also specified.]"
94
"[o:override?Make a writable copy of the probe information file in the"
95
" directory \abindir\a\b/../lib/probe/\b\alang\a/\atool\a and list the"
96
" generated file pathname on the standard output. If \b--key\b is also"
97
" specified then the override path is listed but not created. When"
98
" \bprobe\b-based commands are run with \abindir\a in \b$PATH\b before"
99
" the bin directory of the standard probe information the override"
100
" information will be used. Note that since the override file is user"
101
" writable automatic regeneration is disabled.]:[bindir]"
102
"[s:silent?By default a message is printed on the standard error when probe"
103
" generation starts; \b--silent\b disables this message.]"
104
"[t:test?Start probe generation but do not save the results. Used for"
105
" debugging probe scripts.]"
106
"[v:verify?Each probe information file contains a comment (in the"
107
" \atool\a syntax) that can be used to verify the contents. This"
108
" option does that verification.]"
109
"[D:debug?Start probe generation, do not save the results, and write the"
110
" probe script trace (see \bsh\b(1) \b-x\b) to the standard error.]"
111
112
"\n"
113
"\nlanguage tool processor\n"
114
"\n"
115
"[+FILES]{"
116
" [+lib/probe/\alanguage\a/\atool\a?\atool\a specific information"
117
" directory for \alanguage\a processors.]"
118
" [+$HOME/.probe?Per-user directory when \bprobe\b is installed"
119
" non-set-uid or the probe directory is on a readonly"
120
" filesystem.]"
121
"}"
122
"[+CAVEATS?To allow probe information to be generated and shared among all"
123
" users the executable \alib/probe/probe\a must be set-uid to the owner"
124
" of the \alib/probe\a directory hierarchy and the probe directory"
125
" filesystem must be mounted read-write.]"
126
"[+?Automatic language processor probing is mostly black magic, but then"
127
" so are most language processor implementations.]"
128
129
"[+SEE ALSO?\bcpp\b(1), \bnmake\b(1), \bpathkey\b(3), \bpathprobe\b(3)]"
130
;
131
132
#include <ast.h>
133
#include <ctype.h>
134
#include <error.h>
135
#include <ls.h>
136
#include <preroot.h>
137
#include <proc.h>
138
#include <sig.h>
139
#include <times.h>
140
141
#define BASE_MAX 14
142
143
#define COMMAND "probe"
144
145
#define ATTRIBUTES (1<<0)
146
#define DELETE (1<<1)
147
#define FORCE (1<<2)
148
#define GENERATE (1<<3)
149
#define KEY (1<<4)
150
#define LIST (1<<5)
151
#define OVERRIDE (1<<6)
152
#define SILENT (1<<7)
153
#define TEST (1<<8)
154
#define TRACE (1<<9)
155
#define VERIFY (1<<10)
156
157
static char* tmp;
158
159
static int signals[] = /* signals caught by interrupt() */
160
{
161
SIGINT,
162
SIGTERM,
163
#ifdef SIGALRM
164
SIGALRM,
165
#endif
166
#ifdef SIGHUP
167
SIGHUP,
168
#endif
169
#ifdef SIGQUIT
170
SIGQUIT,
171
#endif
172
};
173
174
#if defined(ST_RDONLY) || defined(ST_NOSUID)
175
176
/*
177
* return non-0 if path is in a readonly or non-setuid fs
178
*/
179
180
static int
181
rofs(const char* path)
182
{
183
struct statvfs vfs;
184
185
if (!statvfs(path, &vfs))
186
{
187
#if defined(ST_RDONLY)
188
if (vfs.f_flag & ST_RDONLY)
189
return 1;
190
#endif
191
#if defined(ST_NOSUID)
192
if (vfs.f_flag & ST_NOSUID)
193
return 1;
194
#endif
195
}
196
return 0;
197
}
198
199
#else
200
201
#define rofs(p) 0
202
203
#endif
204
205
/*
206
* check path for old processor name clash
207
* if old!=0 then file path compared with file old
208
* 0 returned if clash or file path the same as old
209
*/
210
211
static int
212
verify(char* path, char* old, char* processor, int must)
213
{
214
char* ns;
215
char* os;
216
int nz;
217
int oz;
218
int r;
219
Sfio_t* nf;
220
Sfio_t* of;
221
222
r = 0;
223
if (nf = sfopen(NiL, path, "r"))
224
{
225
if ((ns = sfgetr(nf, '\n', 1)) && (nz = sfvalue(nf) - 1) > 0)
226
{
227
ns += nz;
228
if ((oz = strlen(processor)) <= nz && !strcmp(processor, ns - oz))
229
r = 1;
230
else
231
error(2, "%s: %s clashes with %s", path, processor, ns - nz);
232
}
233
if (r && old && sfseek(nf, 0L, 0) == 0 && (of = sfopen(NiL, old, "r")))
234
{
235
for (;;)
236
{
237
ns = sfreserve(nf, 0, 0);
238
nz = sfvalue(nf);
239
os = sfreserve(of, 0, 0);
240
oz = sfvalue(nf);
241
if (nz <= 0 || oz <= 0)
242
break;
243
if (nz > oz)
244
nz = oz;
245
if (memcmp(ns, os, nz))
246
break;
247
nz = sfread(nf, ns, nz);
248
oz = sfread(of, os, nz);
249
if (!nz || !oz)
250
break;
251
}
252
sfclose(of);
253
if (!nz && !oz && !touch(old, (time_t)-1, (time_t)-1, 0))
254
r = 0;
255
}
256
sfclose(nf);
257
}
258
else if (must)
259
error(ERROR_SYSTEM|2, "%s: cannot read", path);
260
return r;
261
}
262
263
/*
264
* yes this is sleazy
265
*/
266
267
static void
268
interrupt(int sig)
269
{
270
if (tmp)
271
remove(tmp);
272
signal(sig, SIG_DFL);
273
kill(getpid(), sig);
274
pause();
275
/*NOTREACHED*/
276
}
277
278
int
279
main(int argc, char** argv)
280
{
281
register int n;
282
register char* base;
283
register char* path;
284
char* script;
285
char* probe;
286
char* language;
287
char* tool;
288
char* processor;
289
char* sentinel;
290
char* s;
291
char* v;
292
char* override;
293
int i;
294
int suid;
295
int options = 0;
296
Proc_t* pp;
297
Sfio_t* fp;
298
Sfio_t* pf;
299
char attributes[PATH_MAX];
300
char cmd[PATH_MAX];
301
char key[BASE_MAX + 1];
302
char* cmdargv[5];
303
char* cmdenvv[2];
304
struct stat ps;
305
struct stat vs;
306
unsigned long ptime;
307
308
NoP(argc);
309
error_info.id = probe = COMMAND;
310
for (;;)
311
{
312
switch (optget(argv, usage))
313
{
314
case 'a':
315
options |= ATTRIBUTES;
316
continue;
317
case 'd':
318
options |= DELETE;
319
continue;
320
case 'f':
321
options |= FORCE;
322
continue;
323
case 'g':
324
options |= GENERATE;
325
continue;
326
case 'k':
327
options |= KEY;
328
continue;
329
case 'l':
330
options |= LIST;
331
continue;
332
case 'o':
333
options |= OVERRIDE;
334
override = opt_info.arg;
335
continue;
336
case 's':
337
options |= SILENT;
338
continue;
339
case 't':
340
options |= TEST;
341
continue;
342
case 'v':
343
options |= VERIFY;
344
continue;
345
case 'D':
346
options |= TRACE;
347
continue;
348
case '?':
349
error(ERROR_USAGE|4, "%s", opt_info.arg);
350
break;
351
case ':':
352
error(2, "%s", opt_info.arg);
353
break;
354
}
355
break;
356
}
357
argv += opt_info.index;
358
if (error_info.errors || !(language = *argv++) || !(tool = *argv++) || !(processor = *argv++) || *argv)
359
error(ERROR_USAGE|4, "%s", optusage(NiL));
360
if (*processor != '/')
361
{
362
if (s = strchr(processor, ' '))
363
{
364
n = s - processor;
365
processor = strncpy(attributes, processor, n);
366
*(processor + n) = 0;
367
}
368
v = processor;
369
if (!(processor = pathpath(processor, NiL, PATH_ABSOLUTE|PATH_REGULAR|PATH_EXECUTE, cmd, sizeof(cmd))))
370
error(3, "%s: processor not found", v);
371
if (s)
372
strcpy(processor + strlen(processor), s);
373
}
374
#if FS_PREROOT
375
if (path = getpreroot(NiL, processor))
376
setpreroot(NiL, path);
377
#endif
378
if (!(path = pathprobe(language, tool, processor, (options & TEST) ? -3 : -2, NiL, 0, attributes, sizeof(attributes))))
379
error(3, "cannot generate probe key");
380
if (stat(path, &ps))
381
{
382
ps.st_mtime = 0;
383
ps.st_mode = 0;
384
}
385
if ((options & TEST) || !(ps.st_mode & S_IWUSR))
386
{
387
/*
388
* verify the hierarchy before any ops
389
*/
390
391
if (!(base = strrchr(path, '/')) || strlen(++base) > BASE_MAX)
392
error(3, "%s: invalid probe path", path);
393
strcpy(key, base);
394
strcpy(base, "../../");
395
strcpy(base + 6, probe);
396
if (stat(path, &vs))
397
error(ERROR_SYSTEM|3, "%s: not found", path);
398
if ((vs.st_mode & S_ISUID) && !rofs(path))
399
{
400
suid = (n = geteuid()) != getuid();
401
if (suid && vs.st_uid != n)
402
error(3, "%s: effective uid mismatch", path);
403
strcpy(base, ".");
404
if (stat(path, &vs))
405
error(ERROR_SYSTEM|3, "%s: not found", path);
406
if (suid && vs.st_uid != n)
407
error(3, "%s: effective uid mismatch", path);
408
}
409
else
410
suid = -1;
411
strcpy(base, probe);
412
if (stat(path, &vs))
413
error(ERROR_SYSTEM|3, "%s: not found", path);
414
ptime = vs.st_mtime;
415
n = strlen(path);
416
if (!(script = newof(0, char, n, 5)))
417
error(ERROR_SYSTEM|3, "out of space");
418
strcpy(script, path);
419
strcpy(script + n, ".ini");
420
if (!stat(script, &vs) && vs.st_size && ptime < (unsigned long)vs.st_mtime)
421
ptime = vs.st_mtime;
422
*(script + n) = 0;
423
if (suid >= 0)
424
strcpy(base, key);
425
else if (!(path = pathprobe(language, tool, processor, -1, NiL, 0, attributes, sizeof(attributes))))
426
error(3, "cannot generate probe key");
427
else
428
{
429
if (stat(path, &ps))
430
{
431
ps.st_mtime = 0;
432
ps.st_mode = 0;
433
}
434
if (!(base = strrchr(path, '/')) || strlen(++base) > BASE_MAX)
435
error(3, "%s: invalid probe path", path);
436
strcpy(key, base);
437
}
438
}
439
440
/*
441
* ops ok now
442
*/
443
444
if (options & DELETE)
445
{
446
if (lstat(processor, &vs) || getuid() != vs.st_uid && (!ps.st_mode || getgid() != ps.st_gid))
447
{
448
/*
449
* request not by owner of processor
450
* so limit to probe owner
451
*/
452
453
setuid(getuid());
454
setgid(getgid());
455
}
456
if (remove(path))
457
error(ERROR_SYSTEM|3, "%s: cannot delete", path);
458
}
459
else
460
{
461
if (!(options & (FORCE|TEST)) && ps.st_mode && (ptime <= (unsigned long)ps.st_mtime || ptime <= (unsigned long)ps.st_ctime || (ps.st_mode & S_IWUSR)))
462
{
463
if (ptime <= (unsigned long)ps.st_mtime || ptime <= (unsigned long)ps.st_ctime)
464
{
465
if (!options)
466
error(1, "%s: information already generated", path);
467
}
468
else if (!(options & OVERRIDE) && (ps.st_mode & S_IWUSR))
469
error(0, "%s probe information for %s language processor %s must be manually regenerated", tool, language, processor);
470
}
471
else if ((options & (FORCE|GENERATE|TEST)) || !(options & ~(FORCE|GENERATE|SILENT|TRACE)))
472
{
473
/*
474
* recursion check using environment sentinel
475
*/
476
477
n = strlen(probe) + strlen(key) + strlen(processor) + 2;
478
if (!(sentinel = newof(0, char, n, 0)))
479
error(ERROR_SYSTEM|3, "out of space");
480
sfsprintf(sentinel, n, "%s%s", probe, key);
481
for (s = sentinel; *s; s++)
482
if (!isalnum(*s))
483
*s = '_';
484
if ((v = getenv(sentinel)) && !strcmp(v, processor))
485
error(3, "%s: recursive probe", processor);
486
sfsprintf(s, n - (s - sentinel), "=%s", processor);
487
488
/*
489
* generate the new info in a tmp file
490
*/
491
492
if (suid < 0)
493
{
494
strcpy(base, ".");
495
if (access(path, F_OK))
496
for (s = path + (*path == '/'); s = strchr(s, '/'); *s++ = '/')
497
{
498
*s = 0;
499
if (access(path, F_OK) && mkdir(path, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH))
500
error(ERROR_SYSTEM|3, "%s: cannot create directory", path);
501
}
502
}
503
if (!(options & SILENT))
504
error(0, "probing %s language processor %s for %s information", language, processor, tool);
505
sfsprintf(base, BASE_MAX + 1, "%s.%06d", probe, getpid());
506
if (!(tmp = strdup(path)))
507
error(ERROR_SYSTEM|3, "out of space");
508
for (n = 0; n < elementsof(signals); n++)
509
if (signal(signals[n], interrupt) == SIG_IGN)
510
signal(signals[n], SIG_IGN);
511
if (!(options & TEST))
512
{
513
if (!(fp = sfopen(NiL, tmp, "w")))
514
error(ERROR_SYSTEM|3, "%s: cannot write", tmp);
515
chmod(tmp, S_IRUSR|S_IRGRP|S_IROTH);
516
}
517
n = 0;
518
cmdargv[n++] = script;
519
if (options & TRACE)
520
cmdargv[n++] = "-d";
521
cmdargv[n++] = processor;
522
cmdargv[n++] = attributes;
523
cmdargv[n] = 0;
524
n = 0;
525
cmdenvv[n++] = sentinel;
526
cmdenvv[n] = 0;
527
if (!(pp = procopen(script, cmdargv, cmdenvv, NiL, PROC_UID|PROC_GID|((options&TEST)?0:PROC_READ))))
528
n = -1;
529
else if (!(options & TEST) && (!(pf = sfnew(NiL, NiL, SF_UNBOUND, pp->rfd, SF_READ)) || !(pp->rfd = -1) || sfmove(pf, fp, SF_UNBOUND, -1) < 0 || sfclose(pf) || sfclose(fp)))
530
n = -2;
531
else
532
n = 0;
533
i = procclose(pp);
534
if (!n)
535
n = i;
536
if (n < -1)
537
error(ERROR_SYSTEM|2, "%s: command io error", script);
538
else if (n == -1)
539
error(ERROR_SYSTEM|2, "%s: command exec error", script);
540
else if (n > 0)
541
error(2, "%s: command exit code %d", script, n);
542
else if (!(options & TEST))
543
{
544
/*
545
* verify and rename to the real probe key path
546
*/
547
548
strcpy(base, key);
549
if (verify(tmp, path, processor, 1))
550
{
551
remove(path);
552
if (!rename(tmp, path))
553
{
554
/*
555
* warp the file to yesterday to
556
* accomodate make installations
557
*/
558
559
if (!stat(path, &ps))
560
{
561
ps.st_mtime = (unsigned long)ps.st_mtime - 24 * 60 * 60;
562
touch(path, ps.st_mtime, ps.st_mtime, 0);
563
}
564
}
565
else if (!verify(path, NiL, processor, 0))
566
{
567
n = -1;
568
error(ERROR_SYSTEM|2, "%s: cannot rename to %s", tmp, path);
569
}
570
}
571
else
572
n = -1;
573
}
574
if (n && !(options & TEST))
575
remove(tmp);
576
}
577
if (!error_info.errors)
578
{
579
if (options & OVERRIDE)
580
{
581
n = 5;
582
base = path + strlen(path);
583
while (base > path && (*--base != '/' || --n));
584
if (n || !strneq(base, "/lib/", 5) || !strneq(base + 5, probe, strlen(probe)))
585
error(3, "%s: probe information already private", path);
586
if (!(v = strrchr(override, '/')))
587
v = override;
588
sfsprintf(cmd, sizeof(cmd), "%-.*s%s", v - override, override, base);
589
if (!(options & KEY))
590
{
591
if (!access(cmd, F_OK))
592
error(3, "%s: override already generated", cmd);
593
if (!(pf = sfopen(NiL, cmd, "w")))
594
{
595
for (s = cmd + (*cmd == '/'); s = strchr(s, '/'); *s++ = '/')
596
{
597
*s = 0;
598
if (access(cmd, F_OK) && mkdir(cmd, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH))
599
error(ERROR_SYSTEM|3, "%s: cannot create override directory", cmd);
600
}
601
if (!(pf = sfopen(NiL, cmd, "w")))
602
error(ERROR_SYSTEM|3, "%s: cannot write", cmd);
603
}
604
if (!(fp = sfopen(NiL, path, "r")))
605
error(ERROR_SYSTEM|3, "%s: cannot read", path);
606
if (sfmove(fp, pf, SF_UNBOUND, -1) < 0 || sfclose(fp) || sfclose(pf))
607
error(ERROR_SYSTEM|3, "%s: copy error", cmd);
608
}
609
sfprintf(sfstdout, "%s\n", cmd);
610
}
611
else
612
{
613
if (options & KEY)
614
sfprintf(sfstdout, "%s\n", path);
615
if (options & ATTRIBUTES)
616
sfprintf(sfstdout, "%s\n", attributes);
617
if (!(options & TEST))
618
{
619
if (options & LIST)
620
{
621
if (!(fp = sfopen(NiL, path, "r")))
622
error(ERROR_SYSTEM|3, "%s: cannot read", path);
623
sfmove(fp, sfstdout, SF_UNBOUND, -1);
624
sfclose(fp);
625
}
626
if (options & VERIFY)
627
verify(path, NiL, processor, 1);
628
}
629
}
630
}
631
}
632
return error_info.errors != 0;
633
}
634
635