Commit | Line | Data |
---|---|---|
5ff67f98 | 1 | /* |
5c709f29 KB |
2 | * Copyright (c) 1980, 1986 The Regents of the University of California. |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms are permitted | |
6 | * provided that the above copyright notice and this paragraph are | |
7 | * duplicated in all such forms and that any documentation, | |
8 | * advertising materials, and other materials related to such | |
9 | * distribution and use acknowledge that the software was developed | |
10 | * by the University of California, Berkeley. The name of the | |
11 | * University may not be used to endorse or promote products derived | |
12 | * from this software without specific prior written permission. | |
13 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR | |
14 | * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED | |
15 | * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. | |
5ff67f98 DF |
16 | */ |
17 | ||
261f5d78 | 18 | #ifndef lint |
5ff67f98 | 19 | char copyright[] = |
5c709f29 | 20 | "@(#) Copyright (c) 1980, 1986 The Regents of the University of California.\n\ |
5ff67f98 | 21 | All rights reserved.\n"; |
5c709f29 | 22 | #endif /* not lint */ |
5ff67f98 DF |
23 | |
24 | #ifndef lint | |
7abf8d65 | 25 | static char sccsid[] = "@(#)savecore.c 5.16 (Berkeley) %G%"; |
5c709f29 | 26 | #endif /* not lint */ |
261f5d78 | 27 | |
d112e9ff | 28 | /* |
97d6da0b | 29 | * savecore |
d112e9ff | 30 | */ |
a3e4f1d7 | 31 | |
d112e9ff BJ |
32 | #include <sys/param.h> |
33 | #include <sys/dir.h> | |
34 | #include <sys/stat.h> | |
ce4fd43b | 35 | #include <sys/time.h> |
865f9d47 | 36 | #include <sys/file.h> |
a8bf8cf3 | 37 | #include <sys/syslog.h> |
f76fd03c KB |
38 | #include <stdio.h> |
39 | #include <nlist.h> | |
7abf8d65 | 40 | #include <paths.h> |
d112e9ff | 41 | |
97d6da0b BJ |
42 | #define DAY (60L*60L*24L) |
43 | #define LEEWAY (3*DAY) | |
44 | ||
45 | #define eq(a,b) (!strcmp(a,b)) | |
261f5d78 | 46 | #ifdef vax |
97d6da0b | 47 | #define ok(number) ((number)&0x7fffffff) |
261f5d78 | 48 | #else |
ce104dfd SL |
49 | #ifdef tahoe |
50 | #define ok(number) ((number)&~0xc0000000) | |
51 | #else | |
261f5d78 SL |
52 | #define ok(number) (number) |
53 | #endif | |
ce104dfd | 54 | #endif |
97d6da0b | 55 | |
a3e4f1d7 | 56 | struct nlist current_nl[] = { /* namelist for currently running system */ |
d112e9ff BJ |
57 | #define X_DUMPDEV 0 |
58 | { "_dumpdev" }, | |
59 | #define X_DUMPLO 1 | |
60 | { "_dumplo" }, | |
61 | #define X_TIME 2 | |
62 | { "_time" }, | |
261f5d78 SL |
63 | #define X_DUMPSIZE 3 |
64 | { "_dumpsize" }, | |
d112e9ff BJ |
65 | #define X_VERSION 4 |
66 | { "_version" }, | |
67 | #define X_PANICSTR 5 | |
68 | { "_panicstr" }, | |
261f5d78 SL |
69 | #define X_DUMPMAG 6 |
70 | { "_dumpmag" }, | |
71 | { "" }, | |
d112e9ff BJ |
72 | }; |
73 | ||
a3e4f1d7 RC |
74 | struct nlist dump_nl[] = { /* name list for dumped system */ |
75 | { "_dumpdev" }, /* entries MUST be the same as */ | |
76 | { "_dumplo" }, /* those in current_nl[] */ | |
77 | { "_time" }, | |
78 | { "_dumpsize" }, | |
79 | { "_version" }, | |
80 | { "_panicstr" }, | |
81 | { "_dumpmag" }, | |
82 | { "" }, | |
83 | }; | |
84 | ||
f6cc4b0d | 85 | char *system; |
97d6da0b BJ |
86 | char *dirname; /* directory to save dumps in */ |
87 | char *ddname; /* name of dump device */ | |
88 | char *find_dev(); | |
89 | dev_t dumpdev; /* dump device */ | |
90 | time_t dumptime; /* time the dump was taken */ | |
91 | int dumplo; /* where dump starts on dumpdev */ | |
261f5d78 SL |
92 | int dumpsize; /* amount of memory dumped */ |
93 | int dumpmag; /* magic number in dump */ | |
97d6da0b BJ |
94 | time_t now; /* current date */ |
95 | char *path(); | |
4d1d39b2 | 96 | char *malloc(); |
97d6da0b BJ |
97 | char *ctime(); |
98 | char vers[80]; | |
99 | char core_vers[80]; | |
100 | char panic_mesg[80]; | |
101 | int panicstr; | |
97d6da0b BJ |
102 | off_t lseek(); |
103 | off_t Lseek(); | |
a3e4f1d7 | 104 | int Verbose; |
c6e3f9fb | 105 | int force; |
99b8650e | 106 | int clear; |
a8bf8cf3 | 107 | extern int errno; |
d112e9ff BJ |
108 | |
109 | main(argc, argv) | |
97d6da0b BJ |
110 | char **argv; |
111 | int argc; | |
d112e9ff | 112 | { |
865f9d47 SL |
113 | char *cp; |
114 | ||
115 | argc--, argv++; | |
116 | while (argc > 0 && argv[0][0] == '-') { | |
117 | for (cp = &argv[0][1]; *cp; cp++) switch (*cp) { | |
97d6da0b | 118 | |
c6e3f9fb MK |
119 | case 'f': |
120 | force++; | |
121 | break; | |
122 | ||
a3e4f1d7 | 123 | case 'v': |
865f9d47 | 124 | Verbose++; |
a3e4f1d7 | 125 | break; |
865f9d47 | 126 | |
99b8650e MK |
127 | case 'c': |
128 | clear++; | |
129 | break; | |
130 | ||
a3e4f1d7 | 131 | default: |
865f9d47 | 132 | usage: |
a3e4f1d7 | 133 | fprintf(stderr, |
c6e3f9fb | 134 | "usage: savecore [-f] [-v] dirname [ system ]\n"); |
a3e4f1d7 RC |
135 | exit(1); |
136 | } | |
865f9d47 | 137 | argc--, argv++; |
d112e9ff | 138 | } |
865f9d47 SL |
139 | if (argc != 1 && argc != 2) |
140 | goto usage; | |
141 | dirname = argv[0]; | |
142 | if (argc == 2) | |
143 | system = argv[1]; | |
a96b688d | 144 | openlog("savecore", LOG_ODELAY, LOG_AUTH); |
865f9d47 | 145 | if (access(dirname, W_OK) < 0) { |
ce104dfd | 146 | Perror(LOG_ERR, "%s: %m", dirname); |
97d6da0b | 147 | exit(1); |
d112e9ff | 148 | } |
d112e9ff | 149 | read_kmem(); |
d112e9ff BJ |
150 | } |
151 | ||
739955be KM |
152 | dump_exists() |
153 | { | |
154 | register int dumpfd; | |
155 | int word; | |
156 | ||
865f9d47 SL |
157 | dumpfd = Open(ddname, O_RDONLY); |
158 | Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_DUMPMAG].n_value)), L_SET); | |
159 | Read(dumpfd, (char *)&word, sizeof (word)); | |
739955be | 160 | close(dumpfd); |
865f9d47 | 161 | if (Verbose && word != dumpmag) { |
f53aabcb | 162 | printf("dumplo = %d (%d bytes)\n", dumplo/DEV_BSIZE, dumplo); |
a3e4f1d7 RC |
163 | printf("magic number mismatch: %x != %x\n", word, dumpmag); |
164 | } | |
261f5d78 | 165 | return (word == dumpmag); |
739955be KM |
166 | } |
167 | ||
168 | clear_dump() | |
169 | { | |
170 | register int dumpfd; | |
171 | int zero = 0; | |
172 | ||
865f9d47 SL |
173 | dumpfd = Open(ddname, O_WRONLY); |
174 | Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_DUMPMAG].n_value)), L_SET); | |
175 | Write(dumpfd, (char *)&zero, sizeof (zero)); | |
739955be KM |
176 | close(dumpfd); |
177 | } | |
178 | ||
97d6da0b BJ |
179 | char * |
180 | find_dev(dev, type) | |
181 | register dev_t dev; | |
182 | register int type; | |
d112e9ff | 183 | { |
d112e9ff | 184 | struct stat statb; |
f0326588 | 185 | char *dp; |
d112e9ff | 186 | |
7abf8d65 | 187 | strcpy(devname, _PATH_DEV); |
97d6da0b | 188 | if (stat(devname, &statb)) { |
d112e9ff | 189 | perror(devname); |
97d6da0b BJ |
190 | continue; |
191 | } | |
192 | if ((statb.st_mode&S_IFMT) != type) | |
193 | continue; | |
194 | if (dev == statb.st_rdev) { | |
4d1d39b2 | 195 | dp = malloc(strlen(devname)+1); |
97d6da0b | 196 | strcpy(dp, devname); |
865f9d47 | 197 | return (dp); |
d112e9ff BJ |
198 | } |
199 | } | |
ce104dfd | 200 | log(LOG_ERR, "Can't find device %d/%d\n", major(dev), minor(dev)); |
97d6da0b BJ |
201 | exit(1); |
202 | /*NOTREACHED*/ | |
d112e9ff BJ |
203 | } |
204 | ||
865f9d47 SL |
205 | int cursyms[] = |
206 | { X_DUMPDEV, X_DUMPLO, X_VERSION, X_DUMPMAG, -1 }; | |
207 | int dumpsyms[] = | |
208 | { X_TIME, X_DUMPSIZE, X_VERSION, X_PANICSTR, X_DUMPMAG, -1 }; | |
d112e9ff BJ |
209 | read_kmem() |
210 | { | |
d112e9ff | 211 | register char *cp; |
865f9d47 | 212 | FILE *fp; |
a3e4f1d7 | 213 | char *dump_sys; |
865f9d47 | 214 | int kmem, i; |
a3e4f1d7 | 215 | |
f76fd03c KB |
216 | dump_sys = system ? system : _PATH_UNIX; |
217 | nlist(_PATH_UNIX, current_nl); | |
a3e4f1d7 | 218 | nlist(dump_sys, dump_nl); |
a3e4f1d7 RC |
219 | /* |
220 | * Some names we need for the currently running system, | |
221 | * others for the system that was running when the dump was made. | |
222 | * The values obtained from the current system are used | |
223 | * to look for things in /dev/kmem that cannot be found | |
224 | * in the dump_sys namelist, but are presumed to be the same | |
225 | * (since the disk partitions are probably the same!) | |
226 | */ | |
865f9d47 SL |
227 | for (i = 0; cursyms[i] != -1; i++) |
228 | if (current_nl[cursyms[i]].n_value == 0) { | |
f76fd03c | 229 | log(LOG_ERR, "%s: %s not in namelist\n", _PATH_UNIX, |
865f9d47 SL |
230 | current_nl[cursyms[i]].n_name); |
231 | exit(1); | |
232 | } | |
233 | for (i = 0; dumpsyms[i] != -1; i++) | |
234 | if (dump_nl[dumpsyms[i]].n_value == 0) { | |
99b8650e | 235 | log(LOG_ERR, "%s: %s not in namelist\n", dump_sys, |
865f9d47 SL |
236 | dump_nl[dumpsyms[i]].n_name); |
237 | exit(1); | |
238 | } | |
f76fd03c | 239 | kmem = Open(_PATH_KMEM, O_RDONLY); |
865f9d47 | 240 | Lseek(kmem, (long)current_nl[X_DUMPDEV].n_value, L_SET); |
261f5d78 | 241 | Read(kmem, (char *)&dumpdev, sizeof (dumpdev)); |
865f9d47 | 242 | Lseek(kmem, (long)current_nl[X_DUMPLO].n_value, L_SET); |
261f5d78 | 243 | Read(kmem, (char *)&dumplo, sizeof (dumplo)); |
865f9d47 | 244 | Lseek(kmem, (long)current_nl[X_DUMPMAG].n_value, L_SET); |
261f5d78 | 245 | Read(kmem, (char *)&dumpmag, sizeof (dumpmag)); |
f53aabcb | 246 | dumplo *= DEV_BSIZE; |
d112e9ff | 247 | ddname = find_dev(dumpdev, S_IFBLK); |
865f9d47 SL |
248 | fp = fdopen(kmem, "r"); |
249 | if (fp == NULL) { | |
99b8650e | 250 | log(LOG_ERR, "Couldn't fdopen kmem\n"); |
97d6da0b | 251 | exit(1); |
d112e9ff | 252 | } |
f6cc4b0d BJ |
253 | if (system) |
254 | return; | |
865f9d47 SL |
255 | fseek(fp, (long)current_nl[X_VERSION].n_value, L_SET); |
256 | fgets(vers, sizeof (vers), fp); | |
d112e9ff | 257 | fclose(fp); |
798fe693 SL |
258 | } |
259 | ||
5638c7c2 SL |
260 | check_kmem() |
261 | { | |
798fe693 SL |
262 | FILE *fp; |
263 | register char *cp; | |
264 | ||
865f9d47 SL |
265 | fp = fopen(ddname, "r"); |
266 | if (fp == NULL) { | |
ce104dfd | 267 | Perror(LOG_ERR, "%s: %m", ddname); |
97d6da0b | 268 | exit(1); |
d112e9ff | 269 | } |
865f9d47 SL |
270 | fseek(fp, (off_t)(dumplo+ok(dump_nl[X_VERSION].n_value)), L_SET); |
271 | fgets(core_vers, sizeof (core_vers), fp); | |
d112e9ff | 272 | fclose(fp); |
99b8650e | 273 | if (!eq(vers, core_vers) && system == 0) { |
f76fd03c | 274 | log(LOG_WARNING, "Warning: %s version mismatch:\n", _PATH_UNIX); |
99b8650e MK |
275 | log(LOG_WARNING, "\t%s\n", vers); |
276 | log(LOG_WARNING, "and\t%s\n", core_vers); | |
277 | } | |
97d6da0b | 278 | fp = fopen(ddname, "r"); |
865f9d47 SL |
279 | fseek(fp, (off_t)(dumplo + ok(dump_nl[X_PANICSTR].n_value)), L_SET); |
280 | fread((char *)&panicstr, sizeof (panicstr), 1, fp); | |
97d6da0b | 281 | if (panicstr) { |
865f9d47 | 282 | fseek(fp, dumplo + ok(panicstr), L_SET); |
d112e9ff BJ |
283 | cp = panic_mesg; |
284 | do | |
285 | *cp = getc(fp); | |
286 | while (*cp++); | |
d112e9ff | 287 | } |
97d6da0b | 288 | fclose(fp); |
f6cc4b0d | 289 | } |
d112e9ff | 290 | |
97d6da0b BJ |
291 | get_crashtime() |
292 | { | |
d112e9ff | 293 | int dumpfd; |
20f89b8d | 294 | time_t clobber = (time_t)0; |
d112e9ff | 295 | |
865f9d47 SL |
296 | dumpfd = Open(ddname, O_RDONLY); |
297 | Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_TIME].n_value)), L_SET); | |
97d6da0b | 298 | Read(dumpfd, (char *)&dumptime, sizeof dumptime); |
d112e9ff | 299 | close(dumpfd); |
a3e4f1d7 RC |
300 | if (dumptime == 0) { |
301 | if (Verbose) | |
c6e3f9fb | 302 | printf("Dump time is zero.\n"); |
261f5d78 | 303 | return (0); |
a3e4f1d7 | 304 | } |
d112e9ff BJ |
305 | printf("System went down at %s", ctime(&dumptime)); |
306 | if (dumptime < now - LEEWAY || dumptime > now + LEEWAY) { | |
865f9d47 | 307 | printf("dump time is unreasonable\n"); |
261f5d78 | 308 | return (0); |
d112e9ff | 309 | } |
261f5d78 | 310 | return (1); |
d112e9ff BJ |
311 | } |
312 | ||
97d6da0b BJ |
313 | char * |
314 | path(file) | |
315 | char *file; | |
d112e9ff | 316 | { |
4d1d39b2 | 317 | register char *cp = malloc(strlen(file) + strlen(dirname) + 2); |
d112e9ff | 318 | |
97d6da0b BJ |
319 | (void) strcpy(cp, dirname); |
320 | (void) strcat(cp, "/"); | |
321 | (void) strcat(cp, file); | |
261f5d78 | 322 | return (cp); |
d112e9ff BJ |
323 | } |
324 | ||
d112e9ff BJ |
325 | check_space() |
326 | { | |
327 | struct stat dsb; | |
328 | register char *ddev; | |
e84afc49 | 329 | int dfd, spacefree; |
798fe693 | 330 | struct fs fs; |
d112e9ff | 331 | |
97d6da0b | 332 | if (stat(dirname, &dsb) < 0) { |
ce104dfd | 333 | Perror(LOG_ERR, "%s: %m", dirname); |
97d6da0b BJ |
334 | exit(1); |
335 | } | |
d112e9ff | 336 | ddev = find_dev(dsb.st_dev, S_IFBLK); |
865f9d47 | 337 | dfd = Open(ddev, O_RDONLY); |
a66ab591 | 338 | Lseek(dfd, SBOFF, L_SET); |
865f9d47 | 339 | Read(dfd, (char *)&fs, sizeof (fs)); |
d112e9ff | 340 | close(dfd); |
865f9d47 SL |
341 | spacefree = freespace(&fs, fs.fs_minfree) * fs.fs_fsize / 1024; |
342 | if (spacefree < read_number("minfree")) { | |
99b8650e | 343 | log(LOG_WARNING, "Dump omitted, not enough space on device\n"); |
97d6da0b | 344 | return (0); |
d112e9ff | 345 | } |
ce104dfd SL |
346 | if (freespace(&fs, fs.fs_minfree) < 0) |
347 | log(LOG_WARNING, | |
99b8650e | 348 | "Dump performed, but free space threshold crossed\n"); |
97d6da0b | 349 | return (1); |
d112e9ff BJ |
350 | } |
351 | ||
d112e9ff | 352 | read_number(fn) |
97d6da0b | 353 | char *fn; |
d112e9ff BJ |
354 | { |
355 | char lin[80]; | |
356 | register FILE *fp; | |
357 | ||
865f9d47 SL |
358 | fp = fopen(path(fn), "r"); |
359 | if (fp == NULL) | |
261f5d78 | 360 | return (0); |
97d6da0b BJ |
361 | if (fgets(lin, 80, fp) == NULL) { |
362 | fclose(fp); | |
261f5d78 | 363 | return (0); |
d112e9ff | 364 | } |
97d6da0b | 365 | fclose(fp); |
261f5d78 | 366 | return (atoi(lin)); |
d112e9ff BJ |
367 | } |
368 | ||
4d1d39b2 MK |
369 | #define BUFPAGES (256*1024/NBPG) /* 1/4 Mb */ |
370 | ||
d112e9ff BJ |
371 | save_core() |
372 | { | |
373 | register int n; | |
4d1d39b2 | 374 | register char *cp; |
d112e9ff BJ |
375 | register int ifd, ofd, bounds; |
376 | register FILE *fp; | |
377 | ||
4d1d39b2 | 378 | cp = malloc(BUFPAGES*NBPG); |
865f9d47 SL |
379 | if (cp == 0) { |
380 | fprintf(stderr, "savecore: Can't allocate i/o buffer.\n"); | |
4d1d39b2 MK |
381 | return; |
382 | } | |
d112e9ff | 383 | bounds = read_number("bounds"); |
f76fd03c | 384 | ifd = Open(system ? system : _PATH_UNIX, O_RDONLY); |
d112e9ff BJ |
385 | while((n = Read(ifd, cp, BUFSIZ)) > 0) |
386 | Write(ofd, cp, n); | |
387 | close(ifd); | |
388 | close(ofd); | |
865f9d47 SL |
389 | ifd = Open(ddname, O_RDONLY); |
390 | Lseek(ifd, (off_t)(dumplo + ok(dump_nl[X_DUMPSIZE].n_value)), L_SET); | |
5638c7c2 | 391 | Read(ifd, (char *)&dumpsize, sizeof (dumpsize)); |
9bd38ba8 | 392 | (void)sprintf(cp, "vmcore.%d", bounds); |
798fe693 | 393 | ofd = Create(path(cp), 0644); |
865f9d47 | 394 | Lseek(ifd, (off_t)dumplo, L_SET); |
ce104dfd SL |
395 | log(LOG_NOTICE, "Saving %d bytes of image in vmcore.%d\n", |
396 | NBPG*dumpsize, bounds); | |
261f5d78 | 397 | while (dumpsize > 0) { |
4d1d39b2 | 398 | n = Read(ifd, cp, |
865f9d47 | 399 | (dumpsize > BUFPAGES ? BUFPAGES : dumpsize) * NBPG); |
f53aabcb | 400 | if (n == 0) { |
99b8650e | 401 | log(LOG_WARNING, "WARNING: vmcore may be incomplete\n"); |
f53aabcb JB |
402 | break; |
403 | } | |
d112e9ff | 404 | Write(ofd, cp, n); |
261f5d78 | 405 | dumpsize -= n/NBPG; |
d112e9ff BJ |
406 | } |
407 | close(ifd); | |
408 | close(ofd); | |
409 | fp = fopen(path("bounds"), "w"); | |
410 | fprintf(fp, "%d\n", bounds+1); | |
411 | fclose(fp); | |
4d1d39b2 | 412 | free(cp); |
d112e9ff BJ |
413 | } |
414 | ||
97d6da0b BJ |
415 | /* |
416 | * Versions of std routines that exit on error. | |
417 | */ | |
97d6da0b BJ |
418 | Open(name, rw) |
419 | char *name; | |
420 | int rw; | |
421 | { | |
422 | int fd; | |
423 | ||
865f9d47 SL |
424 | fd = open(name, rw); |
425 | if (fd < 0) { | |
ce104dfd | 426 | Perror(LOG_ERR, "%s: %m", name); |
97d6da0b BJ |
427 | exit(1); |
428 | } | |
865f9d47 | 429 | return (fd); |
97d6da0b BJ |
430 | } |
431 | ||
432 | Read(fd, buff, size) | |
433 | int fd, size; | |
434 | char *buff; | |
435 | { | |
436 | int ret; | |
437 | ||
865f9d47 SL |
438 | ret = read(fd, buff, size); |
439 | if (ret < 0) { | |
ce104dfd | 440 | Perror(LOG_ERR, "read: %m"); |
97d6da0b BJ |
441 | exit(1); |
442 | } | |
865f9d47 | 443 | return (ret); |
97d6da0b BJ |
444 | } |
445 | ||
446 | off_t | |
447 | Lseek(fd, off, flag) | |
448 | int fd, flag; | |
449 | long off; | |
450 | { | |
451 | long ret; | |
452 | ||
865f9d47 SL |
453 | ret = lseek(fd, off, flag); |
454 | if (ret == -1) { | |
ce104dfd | 455 | Perror(LOG_ERR, "lseek: %m"); |
97d6da0b BJ |
456 | exit(1); |
457 | } | |
865f9d47 | 458 | return (ret); |
97d6da0b BJ |
459 | } |
460 | ||
461 | Create(file, mode) | |
462 | char *file; | |
463 | int mode; | |
464 | { | |
465 | register int fd; | |
466 | ||
865f9d47 SL |
467 | fd = creat(file, mode); |
468 | if (fd < 0) { | |
ce104dfd | 469 | Perror(LOG_ERR, "%s: %m", file); |
97d6da0b BJ |
470 | exit(1); |
471 | } | |
865f9d47 | 472 | return (fd); |
97d6da0b BJ |
473 | } |
474 | ||
475 | Write(fd, buf, size) | |
476 | int fd, size; | |
477 | char *buf; | |
97d6da0b BJ |
478 | { |
479 | ||
480 | if (write(fd, buf, size) < size) { | |
ce104dfd | 481 | Perror(LOG_ERR, "write: %m"); |
97d6da0b BJ |
482 | exit(1); |
483 | } | |
484 | } | |
ce104dfd SL |
485 | |
486 | log(level, msg, a1, a2) | |
487 | int level; | |
488 | char *msg; | |
489 | { | |
490 | ||
491 | fprintf(stderr, msg, a1, a2); | |
492 | syslog(level, msg, a1, a2); | |
493 | } | |
494 | ||
495 | Perror(level, msg, s) | |
496 | int level; | |
497 | char *msg; | |
498 | { | |
499 | int oerrno = errno; | |
500 | ||
501 | perror(s); | |
502 | errno = oerrno; | |
503 | syslog(level, msg, s); | |
504 | } |