|
|
1.1 root 1: /* unixd.c - daemon for UNIX MIB */
2:
3: #ifndef lint
4: static char *rcsid = "$Header: /f/osi/snmp/RCS/unixd.c,v 7.7 90/07/09 14:49:48 mrose Exp $";
5: #endif
6:
7: /*
8: * $Header: /f/osi/snmp/RCS/unixd.c,v 7.7 90/07/09 14:49:48 mrose Exp $
9: *
10: * Contributed by NYSERNet Inc. This work was partially supported by the
11: * U.S. Defense Advanced Research Projects Agency and the Rome Air Development
12: * Center of the U.S. Air Force Systems Command under contract number
13: * F30602-88-C-0016.
14: *
15: *
16: * $Log: unixd.c,v $
17: * Revision 7.7 90/07/09 14:49:48 mrose
18: * sync
19: *
20: * Revision 7.6 90/03/06 13:51:01 mrose
21: * jch
22: *
23: * Revision 7.5 90/02/23 17:48:05 mrose
24: * update
25: *
26: * Revision 7.4 90/02/19 15:54:09 mrose
27: * touch-up
28: *
29: * Revision 7.3 90/02/19 15:39:08 mrose
30: * one more time
31: *
32: * Revision 7.2 90/02/17 17:19:01 mrose
33: * touch-up
34: *
35: * Revision 7.1 90/02/17 10:42:20 mrose
36: * touch-up
37: *
38: * Revision 7.0 90/02/17 10:36:48 mrose
39: * *** empty log message ***
40: *
41: */
42:
43: /*
44: * NOTICE
45: *
46: * Acquisition, use, and distribution of this module and related
47: * materials are subject to the restrictions of a license agreement.
48: * Consult the Preface in the User's Manual for the full terms of
49: * this agreement.
50: *
51: */
52:
53:
54: #include <errno.h>
55: #include <signal.h>
56: #include <stdio.h>
57: #include <varargs.h>
58: #include "smux.h"
59: #include "objects.h"
60: #include <sys/ioctl.h>
61: #ifdef BSD42
62: #include <sys/file.h>
63: #endif
64: #ifdef SYS5
65: #include <fcntl.h>
66: #endif
67: #include "tailor.h"
68:
69: /* DATA */
70:
71: int debug = 0;
72: static int nbits = FD_SETSIZE;
73:
74: static LLog _pgm_log = {
75: "unixd.log", NULLCP, NULLCP, LLOG_FATAL | LLOG_EXCEPTIONS | LLOG_NOTICE,
76: LLOG_FATAL, -1, LLOGCLS | LLOGCRT | LLOGZER, NOTOK
77: };
78: static LLog *pgm_log = &_pgm_log;
79:
80: static char *myname = "unixd";
81:
82:
83: static int smux_fd = NOTOK;
84: static int rock_and_roll = 0;
85: static int dont_bother_anymore = 0;
86:
87: static OID subtree = NULLOID;
88: static struct smuxEntry *se = NULL;
89:
90:
91: static fd_set ifds;
92: static fd_set ofds;
93:
94:
95: void adios (), advise ();
96:
97: /* MAIN */
98:
99: /* ARGSUSED */
100:
101: main (argc, argv, envp)
102: int argc;
103: char **argv,
104: **envp;
105: {
106: int nfds;
107:
108: arginit (argv);
109: envinit ();
110:
111: FD_ZERO (&ifds);
112: FD_ZERO (&ofds);
113: nfds = 0;
114:
115: /* set fd's for other purposes here... */
116:
117: for (;;) {
118: int n,
119: secs;
120: fd_set rfds,
121: wfds;
122:
123: secs = NOTOK;
124:
125: rfds = ifds; /* struct copy */
126: wfds = ofds; /* .. */
127:
128: if (smux_fd == NOTOK && !dont_bother_anymore)
129: secs = 5 * 60L;
130: else
131: if (rock_and_roll)
132: FD_SET (smux_fd, &rfds);
133: else
134: FD_SET (smux_fd, &wfds);
135: if (smux_fd >= nfds)
136: nfds = smux_fd + 1;
137:
138: if ((n = xselect (nfds, &rfds, &wfds, NULLFD, secs)) == NOTOK)
139: adios ("failed", "xselect");
140:
141: /* check fd's for other purposes here... */
142:
143: if (smux_fd == NOTOK && !dont_bother_anymore) {
144: if (n == 0) {
145: if ((smux_fd = smux_init (debug)) == NOTOK)
146: advise (LLOG_EXCEPTIONS, NULLCP, "smux_init: %s [%s]",
147: smux_error (smux_errno), smux_info);
148: else
149: rock_and_roll = 0;
150: }
151: }
152: else
153: if (rock_and_roll) {
154: if (FD_ISSET (smux_fd, &rfds))
155: doit_smux ();
156: }
157: else
158: if (FD_ISSET (smux_fd, &wfds))
159: start_smux ();
160: }
161: }
162:
163: /* MISCELLANY */
164:
165: static arginit (vec)
166: char **vec;
167: {
168: register char *ap;
169:
170: if (myname = rindex (*vec, '/'))
171: myname++;
172: if (myname == NULL || *myname == NULL)
173: myname = *vec;
174: if (strncmp (myname, "smux.", 5) == 0 && myname[5] != NULL)
175: myname += 5;
176:
177: isodetailor (myname, 0);
178: ll_hdinit (pgm_log, myname);
179:
180: for (vec++; ap = *vec; vec++) {
181: if (*ap == '-')
182: switch (*++ap) {
183: case 'd':
184: debug++;
185: continue;
186:
187: default:
188: adios (NULLCP, "-%s: unknown switch", ap);
189: }
190:
191: adios (NULLCP, "usage: %s [switches]", myname);
192: }
193:
194:
195: }
196:
197: /* */
198:
199: static envinit () {
200: int i,
201: sd;
202:
203: nbits = getdtablesize ();
204:
205: if (debug == 0 && !(debug = isatty (2))) {
206: for (i = 0; i < 5; i++) {
207: switch (fork ()) {
208: case NOTOK:
209: sleep (5);
210: continue;
211:
212: case OK:
213: break;
214:
215: default:
216: _exit (0);
217: }
218: break;
219: }
220:
221: (void) chdir ("/");
222:
223: if ((sd = open ("/dev/null", O_RDWR)) == NOTOK)
224: adios ("/dev/null", "unable to read");
225: if (sd != 0)
226: (void) dup2 (sd, 0), (void) close (sd);
227: (void) dup2 (0, 1);
228: (void) dup2 (0, 2);
229:
230: #ifdef SETSID
231: if (setsid () == NOTOK)
232: advise (LLOG_EXCEPTIONS, "failed", "setsid");
233: #endif
234: #ifdef TIOCNOTTY
235: if ((sd = open ("/dev/tty", O_RDWR)) != NOTOK) {
236: (void) ioctl (sd, TIOCNOTTY, NULLCP);
237: (void) close (sd);
238: }
239: #else
240: #ifdef SYS5
241: (void) setpgrp ();
242: (void) signal (SIGINT, SIG_IGN);
243: (void) signal (SIGQUIT, SIG_IGN);
244: #endif
245: #endif
246: }
247: else
248: ll_dbinit (pgm_log, myname);
249:
250: #ifndef sun /* damn YP... */
251: for (sd = 3; sd < nbits; sd++)
252: if (pgm_log -> ll_fd != sd)
253: (void) close (sd);
254: #endif
255:
256: (void) signal (SIGPIPE, SIG_IGN);
257:
258: ll_hdinit (pgm_log, myname);
259:
260: mibinit ();
261:
262: advise (LLOG_NOTICE, NULLCP, "starting");
263: }
264:
265: /* MIB */
266:
267: #include <nlist.h>
268:
269: static int kd;
270: static int quantum = 0;
271: static int lastq = -1;
272:
273: static struct nlist nl[] = {
274: #define N_MBSTAT 0
275: { "_mbstat" },
276:
277: NULL
278: };
279:
280:
281: static mibinit () {
282: OT ot;
283: register struct nlist *nz;
284:
285: if ((se = getsmuxEntrybyname ("unixd")) == NULL)
286: adios (NULLCP, "no SMUX entry for \"%s\"", "unixd");
287:
288: if (readobjects ("unixd.defs") == NOTOK)
289: adios (NULLCP, "readobjects: %s", PY_pepy);
290:
291: if ((ot = text2obj ("mbuf")) == NULL)
292: adios (NULLCP, "text2obj (\"%s\") fails", "mbuf");
293: subtree = ot -> ot_name;
294:
295: if (nlist ("/vmunix", nl) == NOTOK)
296: adios ("/vmunix", "unable to nlist");
297: for (nz = nl; nz -> n_name; nz++)
298: if (nz -> n_value == 0)
299: advise (LLOG_EXCEPTIONS, NULLCP, "\"%s\" not in /vmunix (warning)",
300: nz -> n_name);
301:
302: if ((kd = open ("/dev/kmem", O_RDONLY)) == NOTOK)
303: adios ("/vmunix", "unable to read");
304:
305: init_unix (); /* UNIX-specific enterprise */
306:
307: if ((smux_fd = smux_init (debug)) == NOTOK)
308: advise (LLOG_EXCEPTIONS, NULLCP, "smux_init: %s [%s]",
309: smux_error (smux_errno), smux_info);
310: else
311: rock_and_roll = 0;
312: }
313:
314: /* */
315:
316: static start_smux () {
317: if (smux_simple_open (&se -> se_identity, "SMUX UNIX daemon",
318: se -> se_password, strlen (se -> se_password))
319: == NOTOK) {
320: if (smux_errno == inProgress)
321: return;
322:
323: advise (LLOG_EXCEPTIONS, NULLCP, "smux_simple_open: %s [%s]",
324: smux_error (smux_errno), smux_info);
325: losing: ;
326: smux_fd = NOTOK;
327: return;
328: }
329: advise (LLOG_NOTICE, NULLCP, "SMUX open: %s \"%s\"",
330: oid2ode (&se -> se_identity), se -> se_name);
331: rock_and_roll = 1;
332:
333: if (smux_register (subtree, -1, readOnly) == NOTOK) {
334: advise (LLOG_EXCEPTIONS, NULLCP, "smux_register: %s [%s]",
335: smux_error (smux_errno), smux_info);
336: goto losing;
337: }
338: advise (LLOG_NOTICE, NULLCP, "SMUX register: readOnly %s in=%d",
339: oid2ode (subtree), -1);
340: }
341:
342: /* */
343:
344: static doit_smux () {
345: struct type_SNMP_SMUX__PDUs *event;
346:
347: if (smux_wait (&event, NOTOK) == NOTOK) {
348: if (smux_errno == inProgress)
349: return;
350:
351: advise (LLOG_EXCEPTIONS, NULLCP, "smux_wait: %s [%s]",
352: smux_error (smux_errno), smux_info);
353: losing: ;
354: smux_fd = NOTOK;
355: return;
356: }
357:
358: switch (event -> offset) {
359: case type_SNMP_SMUX__PDUs_registerResponse:
360: {
361: struct type_SNMP_RRspPDU *rsp = event -> un.registerResponse;
362:
363: if (rsp -> parm == int_SNMP_RRspPDU_failure) {
364: advise (LLOG_NOTICE, NULLCP,
365: "SMUX registration of %s failed",
366: oid2ode (subtree));
367: dont_bother_anymore = 1;
368: (void) smux_close (goingDown);
369: goto losing;
370: }
371: else
372: advise (LLOG_NOTICE, NULLCP,
373: "SMUX register: readOnly %s out=%d",
374: oid2ode (subtree), rsp -> parm);
375: }
376: if (smux_trap (int_SNMP_generic__trap_coldStart,
377: 0, (struct type_SNMP_VarBindList *) 0) == NOTOK) {
378: advise (LLOG_EXCEPTIONS, NULLCP, "smux_trap: %s [%s]",
379: smux_error (smux_errno), smux_info);
380: goto losing;
381: }
382: break;
383:
384: case type_SNMP_SMUX__PDUs_get__request:
385: case type_SNMP_SMUX__PDUs_get__next__request:
386: get_smux (event -> un.get__request, event -> offset);
387: break;
388:
389: case type_SNMP_SMUX__PDUs_close:
390: advise (LLOG_NOTICE, NULLCP, "SMUX close: %s",
391: smux_error (event -> un.close -> parm));
392: goto losing;
393:
394: case type_SNMP_SMUX__PDUs_simple:
395: case type_SNMP_SMUX__PDUs_registerRequest:
396: case type_SNMP_SMUX__PDUs_get__response:
397: case type_SNMP_SMUX__PDUs_set__request:
398: case type_SNMP_SMUX__PDUs_trap:
399: advise (LLOG_EXCEPTIONS, NULLCP, "unexpectedOperation: %d",
400: event -> offset);
401: (void) smux_close (protocolError);
402: goto losing;
403:
404: default:
405: advise (LLOG_EXCEPTIONS, NULLCP, "badOperation: %d",
406: event -> offset);
407: (void) smux_close (protocolError);
408: goto losing;
409: }
410: }
411:
412: /* */
413:
414: static get_smux (pdu, offset)
415: register struct type_SNMP_GetRequest__PDU *pdu;
416: int offset;
417: {
418: int idx,
419: status;
420: object_instance ois;
421: register struct type_SNMP_VarBindList *vp;
422:
423: quantum = pdu -> request__id;
424: idx = 0;
425: for (vp = pdu -> variable__bindings; vp; vp = vp -> next) {
426: register OI oi;
427: register OT ot;
428: register struct type_SNMP_VarBind *v = vp -> VarBind;
429:
430: idx++;
431:
432: if (offset == type_SNMP_SMUX__PDUs_get__next__request) {
433: if ((oi = name2inst (v -> name)) == NULLOI
434: && (oi = next2inst (v -> name)) == NULLOI)
435: goto no_name;
436:
437: if ((ot = oi -> oi_type) -> ot_getfnx == NULLIFP)
438: goto get_next;
439: }
440: else
441: if ((oi = name2inst (v -> name)) == NULLOI
442: || (ot = oi -> oi_type) -> ot_getfnx == NULLIFP) {
443: no_name: ;
444: pdu -> error__status = int_SNMP_error__status_noSuchName;
445: goto out;
446: }
447:
448: try_again: ;
449: switch (ot -> ot_access) {
450: case OT_NONE:
451: if (offset == type_SNMP_SMUX__PDUs_get__next__request)
452: goto get_next;
453: goto no_name;
454:
455: case OT_RDONLY:
456: if (offset == type_SNMP_SMUX__PDUs_set__request) {
457: pdu -> error__status = int_SNMP_error__status_readOnly;
458: goto out;
459: }
460: break;
461:
462: case OT_RDWRITE:
463: break;
464: }
465:
466: switch (status = (*ot -> ot_getfnx) (oi, v, offset)) {
467: case NOTOK: /* get-next wants a bump */
468: get_next: ;
469: oi = &ois;
470: for (;;) {
471: if ((ot = ot -> ot_next) == NULLOT) {
472: pdu -> error__status =
473: int_SNMP_error__status_noSuchName;
474: goto out;
475: }
476: oi -> oi_name = (oi -> oi_type = ot) -> ot_name;
477: if (ot -> ot_getfnx)
478: goto try_again;
479: }
480:
481: case int_SNMP_error__status_noError:
482: break;
483:
484: default:
485: pdu -> error__status = status;
486: goto out;
487: }
488: }
489: idx = 0;
490:
491: out: ;
492: pdu -> error__index = idx;
493:
494: if (smux_response (pdu) == NOTOK) {
495: advise (LLOG_EXCEPTIONS, NULLCP, "smux_response: %s [%s]",
496: smux_error (smux_errno), smux_info);
497: smux_fd = NOTOK;
498: }
499: }
500:
501: /* UNIX */
502:
503: #ifdef BSD44
504: #include <sys/param.h>
505: #include <machine/endian.h>
506: #include <machine/machparam.h>
507: #endif
508: #include <sys/mbuf.h>
509:
510: /* */
511:
512: static struct mbstat mbstat;
513:
514: /* */
515:
516: #define mbufS 0
517: #define mbufClusters 1
518: #define mbufFreeClusters 2
519: #define mbufDrops 3
520: #ifdef BSD44
521: #define mbufWaits 4
522: #define mbufDrains 5
523: #endif
524: #if !defined(BSD43) && !defined(BSD44)
525: #define mbufFrees 6
526: #endif
527:
528:
529: static int o_mbuf (oi, v, offset)
530: OI oi;
531: register struct type_SNMP_VarBind *v;
532: int offset;
533: {
534: int ifvar;
535: register struct mbstat *m = &mbstat;
536: register OID oid = oi -> oi_name;
537: register OT ot = oi -> oi_type;
538:
539: ifvar = (int) ot -> ot_info;
540: switch (offset) {
541: case type_SNMP_SMUX__PDUs_get__request:
542: if (oid -> oid_nelem != ot -> ot_name -> oid_nelem + 1
543: || oid -> oid_elements[oid -> oid_nelem - 1] != 0)
544: return int_SNMP_error__status_noSuchName;
545: break;
546:
547: case type_SNMP_SMUX__PDUs_get__next__request:
548: if (oid -> oid_nelem == ot -> ot_name -> oid_nelem) {
549: OID new;
550:
551: if ((new = oid_extend (oid, 1)) == NULLOID)
552: return int_SNMP_error__status_genErr;
553: new -> oid_elements[new -> oid_nelem - 1] = 0;
554:
555: if (v -> name)
556: free_SNMP_ObjectName (v -> name);
557: v -> name = new;
558: }
559: else
560: return NOTOK;
561: break;
562:
563: default:
564: return int_SNMP_error__status_genErr;
565: }
566:
567: if (quantum != lastq) {
568: lastq = quantum;
569:
570: if (getkmem (nl + N_MBSTAT, (caddr_t) m, sizeof *m) == NOTOK)
571: return int_SNMP_error__status_genErr;
572: }
573:
574: switch (ifvar) {
575: case mbufS:
576: return o_integer (oi, v, m -> m_mbufs);
577:
578: case mbufClusters:
579: return o_integer (oi, v, m -> m_clusters);
580:
581: case mbufFreeClusters:
582: return o_integer (oi, v, m -> m_clfree);
583:
584: case mbufDrops:
585: return o_integer (oi, v, m -> m_drops);
586:
587: #ifdef mbufWaits
588: case mbufWaits:
589: return o_integer (oi, v, m -> m_wait);
590: #endif
591:
592: #ifdef mbufDrains
593: case mbufDrains:
594: return o_integer (oi, v, m -> m_drain);
595: #endif
596:
597: #ifdef mbufFrees:
598: case mbufFrees:
599: return o_integer (oi, v, m -> m_mbfree);
600: #endif
601:
602: default:
603: return int_SNMP_error__status_noSuchName;
604: }
605: }
606:
607: /* */
608:
609: #define mbufType 0
610: #define mbufAllocates 1
611:
612:
613: static int o_mbufType (oi, v, offset)
614: OI oi;
615: register struct type_SNMP_VarBind *v;
616: int offset;
617: {
618: int ifnum,
619: ifvar;
620: register struct mbstat *m = &mbstat;
621: register OID oid = oi -> oi_name;
622: register OT ot = oi -> oi_type;
623:
624: ifvar = (int) ot -> ot_info;
625: switch (offset) {
626: case type_SNMP_SMUX__PDUs_get__request:
627: if (oid -> oid_nelem != ot -> ot_name -> oid_nelem + 1)
628: return int_SNMP_error__status_noSuchName;
629: if ((ifnum = oid -> oid_elements[oid -> oid_nelem - 1])
630: >= sizeof m -> m_mtypes / sizeof m -> m_mtypes[0])
631: return int_SNMP_error__status_noSuchName;
632: break;
633:
634: case type_SNMP_SMUX__PDUs_get__next__request:
635: again: ;
636: if (oid -> oid_nelem == ot -> ot_name -> oid_nelem) {
637: OID new;
638:
639: ifnum = 0;
640:
641: if ((new = oid_extend (oid, 1)) == NULLOID)
642: return int_SNMP_error__status_genErr;
643: new -> oid_elements[new -> oid_nelem - 1] = ifnum;
644:
645: if (v -> name)
646: free_SNMP_ObjectName (v -> name);
647: v -> name = new;
648:
649: oid = new; /* for hack... */
650: }
651: else {
652: int i = ot -> ot_name -> oid_nelem;
653:
654: if ((ifnum = oid -> oid_elements[i] + 1)
655: >= sizeof m -> m_mtypes / sizeof m -> m_mtypes[0])
656: return NOTOK;
657:
658: oid -> oid_elements[i] = ifnum;
659: oid -> oid_nelem = i + 1;
660: }
661: break;
662:
663: default:
664: return int_SNMP_error__status_genErr;
665: }
666:
667: if (quantum != lastq) {
668: lastq = quantum;
669:
670: if (getkmem (nl + N_MBSTAT, (caddr_t) m, sizeof *m) == NOTOK)
671: return int_SNMP_error__status_genErr;
672: }
673:
674: /* hack to compress table size... */
675: if (offset == type_SNMP_SMUX__PDUs_get__next__request
676: && m -> m_mtypes[ifnum] == 0)
677: goto again;
678:
679: switch (ifvar) {
680: case mbufType:
681: return o_integer (oi, v, ifnum);
682:
683: case mbufAllocates:
684: return o_integer (oi, v, m -> m_mtypes[ifnum]);
685:
686: default:
687: return int_SNMP_error__status_noSuchName;
688: }
689: }
690:
691: /* */
692:
693: init_unix () {
694: register OT ot;
695:
696: if (ot = text2obj ("mbufS"))
697: ot -> ot_getfnx = o_mbuf,
698: ot -> ot_info = (caddr_t) mbufS;
699: if (ot = text2obj ("mbufClusters"))
700: ot -> ot_getfnx = o_mbuf,
701: ot -> ot_info = (caddr_t) mbufClusters;
702: if (ot = text2obj ("mbufFreeClusters"))
703: ot -> ot_getfnx = o_mbuf,
704: ot -> ot_info = (caddr_t) mbufFreeClusters;
705: if (ot = text2obj ("mbufDrops"))
706: ot -> ot_getfnx = o_mbuf,
707: ot -> ot_info = (caddr_t) mbufDrops;
708: #ifdef mbufWaits
709: if (ot = text2obj ("mbufWaits"))
710: ot -> ot_getfnx = o_mbuf,
711: ot -> ot_info = (caddr_t) mbufWaits;
712: #endif
713: #ifdef mbufDrains
714: if (ot = text2obj ("mbufDrains"))
715: ot -> ot_getfnx = o_mbuf,
716: ot -> ot_info = (caddr_t) mbufDrains;
717: #endif
718: #ifdef mbufFrees
719: if (ot = text2obj ("mbufFrees"))
720: ot -> ot_getfnx = o_mbuf,
721: ot -> ot_info = (caddr_t) mbufFrees;
722: #endif
723: if (ot = text2obj ("mbufType"))
724: ot -> ot_getfnx = o_mbufType,
725: ot -> ot_info = (caddr_t) mbufType;
726: if (ot = text2obj ("mbufAllocates"))
727: ot -> ot_getfnx = o_mbufType,
728: ot -> ot_info = (caddr_t) mbufAllocates;
729: }
730:
731: /* */
732:
733: int getkmem (n, buffer, cc)
734: struct nlist *n;
735: caddr_t buffer;
736: int cc;
737: {
738: if (n -> n_value == 0) {
739: advise (LLOG_EXCEPTIONS, NULLCP, "\"%s\" not in /vmunix", n -> n_name);
740: return NOTOK;
741: }
742: if (lseek (kd, (long) n -> n_value, L_SET) == NOTOK) {
743: advise (LLOG_EXCEPTIONS, "failed", "lseek of 0x%x for \"%s\" in kmem",
744: (long) n -> n_value, n -> n_name);
745: return NOTOK;
746: }
747: if (read (kd, buffer, cc) != cc) {
748: advise (LLOG_EXCEPTIONS, "failed", "read of \"%s\" from kmem",
749: n -> n_name);
750: return NOTOK;
751: }
752:
753: return OK;
754: }
755:
756: /* ERRORS */
757:
758: #ifndef lint
759: void adios (va_alist)
760: va_dcl
761: {
762: va_list ap;
763:
764: va_start (ap);
765:
766: _ll_log (pgm_log, LLOG_FATAL, ap);
767:
768: va_end (ap);
769:
770: _exit (1);
771: }
772: #else
773: /* VARARGS */
774:
775: void adios (what, fmt)
776: char *what,
777: *fmt;
778: {
779: adios (what, fmt);
780: }
781: #endif
782:
783:
784: #ifndef lint
785: void advise (va_alist)
786: va_dcl
787: {
788: int code;
789: va_list ap;
790:
791: va_start (ap);
792:
793: code = va_arg (ap, int);
794:
795: _ll_log (pgm_log, code, ap);
796:
797: va_end (ap);
798: }
799: #else
800: /* VARARGS */
801:
802: void advise (code, what, fmt)
803: char *what,
804: *fmt;
805: int code;
806: {
807: advise (code, what, fmt);
808: }
809: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.