]>
Commit | Line | Data |
---|---|---|
1815bff5 A |
1 | /* |
2 | * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
2fc1e207 A |
6 | * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights |
7 | * Reserved. This file contains Original Code and/or Modifications of | |
8 | * Original Code as defined in and that are subject to the Apple Public | |
9 | * Source License Version 1.0 (the 'License'). You may not use this file | |
10 | * except in compliance with the License. Please obtain a copy of the | |
11 | * License at http://www.apple.com/publicsource and read it before using | |
12 | * this file. | |
1815bff5 A |
13 | * |
14 | * The Original Code and all software distributed under the License are | |
15 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
16 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
17 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
2fc1e207 A |
18 | * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the |
19 | * License for the specific language governing rights and limitations | |
20 | * under the License." | |
1815bff5 A |
21 | * |
22 | * @APPLE_LICENSE_HEADER_END@ | |
23 | */ | |
24 | /* | |
25 | * Copyright (c) 1983, 1992, 1993 | |
26 | * The Regents of the University of California. All rights reserved. | |
27 | * | |
28 | * Redistribution and use in source and binary forms, with or without | |
29 | * modification, are permitted provided that the following conditions | |
30 | * are met: | |
31 | * 1. Redistributions of source code must retain the above copyright | |
32 | * notice, this list of conditions and the following disclaimer. | |
33 | * 2. Redistributions in binary form must reproduce the above copyright | |
34 | * notice, this list of conditions and the following disclaimer in the | |
35 | * documentation and/or other materials provided with the distribution. | |
36 | * 3. All advertising materials mentioning features or use of this software | |
37 | * must display the following acknowledgement: | |
38 | * This product includes software developed by the University of | |
39 | * California, Berkeley and its contributors. | |
40 | * 4. Neither the name of the University nor the names of its contributors | |
41 | * may be used to endorse or promote products derived from this software | |
42 | * without specific prior written permission. | |
43 | * | |
44 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
45 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
46 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
47 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
48 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
49 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
50 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
51 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
52 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
53 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
54 | * SUCH DAMAGE. | |
55 | */ | |
56 | ||
57 | #ifndef lint | |
58 | static char copyright[] = | |
59 | "@(#) Copyright (c) 1983, 1992, 1993\n\ | |
60 | The Regents of the University of California. All rights reserved.\n"; | |
61 | #endif /* not lint */ | |
62 | ||
63 | #ifndef lint | |
64 | static char sccsid[] = "@(#)kgmon.c 8.1 (Berkeley) 6/6/93"; | |
65 | #endif /* not lint */ | |
66 | ||
67 | #include <sys/param.h> | |
68 | #include <sys/file.h> | |
69 | #include <sys/sysctl.h> | |
70 | #include <sys/gmon.h> | |
71 | #include <errno.h> | |
72 | #include <kvm.h> | |
73 | #include <limits.h> | |
74 | #include <stdio.h> | |
75 | #include <stdlib.h> | |
76 | #include <string.h> | |
77 | #include <nlist.h> | |
78 | #include <ctype.h> | |
79 | #include <paths.h> | |
80 | ||
81 | struct nlist nl[] = { | |
82 | #define N_GMONPARAM 0 | |
83 | { "__gmonparam" }, | |
84 | #define N_PROFHZ 1 | |
85 | { "_profhz" }, | |
86 | 0, | |
87 | }; | |
88 | ||
89 | struct kvmvars { | |
90 | kvm_t *kd; | |
91 | struct gmonparam gpm; | |
92 | }; | |
93 | ||
94 | int bflag, hflag, kflag, rflag, pflag; | |
95 | int debug = 0; | |
96 | void setprof __P((struct kvmvars *kvp, int state)); | |
97 | void dumpstate __P((struct kvmvars *kvp)); | |
98 | void reset __P((struct kvmvars *kvp)); | |
99 | ||
100 | int | |
101 | main(int argc, char **argv) | |
102 | { | |
103 | extern char *optarg; | |
104 | extern int optind; | |
105 | int ch, mode, disp, accessmode; | |
106 | struct kvmvars kvmvars; | |
107 | char *system, *kmemf; | |
108 | ||
109 | seteuid(getuid()); | |
110 | kmemf = NULL; | |
111 | system = NULL; | |
112 | while ((ch = getopt(argc, argv, "M:N:bhpr")) != EOF) { | |
113 | switch((char)ch) { | |
114 | ||
115 | case 'M': | |
116 | kmemf = optarg; | |
117 | kflag = 1; | |
118 | break; | |
119 | ||
120 | case 'N': | |
121 | system = optarg; | |
122 | break; | |
123 | ||
124 | case 'b': | |
125 | bflag = 1; | |
126 | break; | |
127 | ||
128 | case 'h': | |
129 | hflag = 1; | |
130 | break; | |
131 | ||
132 | case 'p': | |
133 | pflag = 1; | |
134 | break; | |
135 | ||
136 | case 'r': | |
137 | rflag = 1; | |
138 | break; | |
139 | ||
140 | default: | |
141 | (void)fprintf(stderr, | |
142 | "usage: kgmon [-bhrp] [-M core] [-N system]\n"); | |
143 | exit(1); | |
144 | } | |
145 | } | |
146 | argc -= optind; | |
147 | argv += optind; | |
148 | ||
149 | #define BACKWARD_COMPATIBILITY | |
150 | #ifdef BACKWARD_COMPATIBILITY | |
151 | if (*argv) { | |
152 | system = *argv; | |
153 | if (*++argv) { | |
154 | kmemf = *argv; | |
155 | ++kflag; | |
156 | } | |
157 | } | |
158 | #endif | |
159 | if (system == NULL) | |
160 | system = _PATH_UNIX; | |
161 | accessmode = openfiles(system, kmemf, &kvmvars); | |
162 | mode = getprof(&kvmvars); | |
163 | if (hflag) | |
164 | disp = GMON_PROF_OFF; | |
165 | else if (bflag) | |
166 | disp = GMON_PROF_ON; | |
167 | else | |
168 | disp = mode; | |
169 | if (pflag) | |
170 | dumpstate(&kvmvars); | |
171 | if (rflag) | |
172 | reset(&kvmvars); | |
173 | if (accessmode == O_RDWR) | |
174 | setprof(&kvmvars, disp); | |
175 | (void)fprintf(stdout, "kgmon: kernel profiling is %s.\n", | |
176 | disp == GMON_PROF_OFF ? "off" : "running"); | |
177 | return (0); | |
178 | } | |
179 | ||
180 | /* | |
181 | * Check that profiling is enabled and open any ncessary files. | |
182 | */ | |
183 | openfiles(system, kmemf, kvp) | |
184 | char *system; | |
185 | char *kmemf; | |
186 | struct kvmvars *kvp; | |
187 | { | |
188 | int mib[3], state, size, openmode; | |
189 | char errbuf[_POSIX2_LINE_MAX]; | |
190 | ||
191 | if (!kflag) { | |
192 | mib[0] = CTL_KERN; | |
193 | mib[1] = KERN_PROF; | |
194 | mib[2] = GPROF_STATE; | |
195 | size = sizeof state; | |
196 | if (sysctl(mib, 3, &state, &size, NULL, 0) < 0) { | |
197 | (void)fprintf(stderr, | |
198 | "kgmon: profiling not defined in kernel.\n"); | |
199 | exit(20); | |
200 | } | |
201 | if (!(bflag || hflag || rflag || | |
202 | (pflag && state == GMON_PROF_ON))) | |
203 | return (O_RDONLY); | |
204 | (void)seteuid(0); | |
205 | if (sysctl(mib, 3, NULL, NULL, &state, size) >= 0) | |
206 | return (O_RDWR); | |
207 | (void)seteuid(getuid()); | |
208 | kern_readonly(state); | |
209 | return (O_RDONLY); | |
210 | } | |
211 | openmode = (bflag || hflag || pflag || rflag) ? O_RDWR : O_RDONLY; | |
212 | kvp->kd = kvm_openfiles(system, kmemf, NULL, openmode, errbuf); | |
213 | if (kvp->kd == NULL) { | |
214 | if (openmode == O_RDWR) { | |
215 | openmode = O_RDONLY; | |
216 | kvp->kd = kvm_openfiles(system, kmemf, NULL, O_RDONLY, | |
217 | errbuf); | |
218 | } | |
219 | if (kvp->kd == NULL) { | |
220 | (void)fprintf(stderr, "kgmon: kvm_openfiles: %s\n", | |
221 | errbuf); | |
222 | exit(2); | |
223 | } | |
224 | kern_readonly(GMON_PROF_ON); | |
225 | } | |
226 | if (kvm_nlist(kvp->kd, nl) < 0) { | |
227 | (void)fprintf(stderr, "kgmon: %s: no namelist\n", system); | |
228 | exit(3); | |
229 | } | |
230 | if (!nl[N_GMONPARAM].n_value) { | |
231 | (void)fprintf(stderr, | |
232 | "kgmon: profiling not defined in kernel.\n"); | |
233 | exit(20); | |
234 | } | |
235 | return (openmode); | |
236 | } | |
237 | ||
238 | /* | |
239 | * Suppress options that require a writable kernel. | |
240 | */ | |
241 | kern_readonly(mode) | |
242 | int mode; | |
243 | { | |
244 | ||
245 | (void)fprintf(stderr, "kgmon: kernel read-only: "); | |
246 | if (pflag && mode == GMON_PROF_ON) | |
247 | (void)fprintf(stderr, "data may be inconsistent\n"); | |
248 | if (rflag) | |
249 | (void)fprintf(stderr, "-r supressed\n"); | |
250 | if (bflag) | |
251 | (void)fprintf(stderr, "-b supressed\n"); | |
252 | if (hflag) | |
253 | (void)fprintf(stderr, "-h supressed\n"); | |
254 | rflag = bflag = hflag = 0; | |
255 | } | |
256 | ||
257 | /* | |
258 | * Get the state of kernel profiling. | |
259 | */ | |
260 | getprof(kvp) | |
261 | struct kvmvars *kvp; | |
262 | { | |
263 | int mib[3], size; | |
264 | ||
265 | if (kflag) { | |
266 | size = kvm_read(kvp->kd, nl[N_GMONPARAM].n_value, &kvp->gpm, | |
267 | sizeof kvp->gpm); | |
268 | } else { | |
269 | mib[0] = CTL_KERN; | |
270 | mib[1] = KERN_PROF; | |
271 | mib[2] = GPROF_GMONPARAM; | |
272 | size = sizeof kvp->gpm; | |
273 | if (sysctl(mib, 3, &kvp->gpm, &size, NULL, 0) < 0) | |
274 | size = 0; | |
275 | } | |
276 | if (size != sizeof kvp->gpm) { | |
277 | (void)fprintf(stderr, "kgmon: cannot get gmonparam: %s\n", | |
278 | kflag ? kvm_geterr(kvp->kd) : strerror(errno)); | |
279 | exit (4); | |
280 | } | |
281 | return (kvp->gpm.state); | |
282 | } | |
283 | ||
284 | /* | |
285 | * Enable or disable kernel profiling according to the state variable. | |
286 | */ | |
287 | void | |
288 | setprof(kvp, state) | |
289 | struct kvmvars *kvp; | |
290 | int state; | |
291 | { | |
292 | struct gmonparam *p = (struct gmonparam *)nl[N_GMONPARAM].n_value; | |
293 | int mib[3], sz, oldstate; | |
294 | ||
295 | sz = sizeof(state); | |
296 | if (!kflag) { | |
297 | mib[0] = CTL_KERN; | |
298 | mib[1] = KERN_PROF; | |
299 | mib[2] = GPROF_STATE; | |
300 | if (sysctl(mib, 3, &oldstate, &sz, NULL, 0) < 0) | |
301 | goto bad; | |
302 | if (oldstate == state) | |
303 | return; | |
304 | (void)seteuid(0); | |
305 | if (sysctl(mib, 3, NULL, NULL, &state, sz) >= 0) { | |
306 | (void)seteuid(getuid()); | |
307 | return; | |
308 | } | |
309 | (void)seteuid(getuid()); | |
310 | } else if (kvm_write(kvp->kd, (u_long)&p->state, (void *)&state, sz) | |
311 | == sz) | |
312 | return; | |
313 | bad: | |
314 | (void)fprintf(stderr, "kgmon: warning: cannot turn profiling %s\n", | |
315 | state == GMON_PROF_OFF ? "off" : "on"); | |
316 | } | |
317 | ||
318 | /* | |
319 | * Build the gmon.out file. | |
320 | */ | |
321 | void | |
322 | dumpstate(kvp) | |
323 | struct kvmvars *kvp; | |
324 | { | |
325 | register FILE *fp; | |
326 | struct rawarc rawarc; | |
327 | struct tostruct *tos; | |
328 | u_long frompc, addr; | |
329 | u_short *froms, *tickbuf; | |
330 | int mib[3], i; | |
331 | struct gmonhdr h; | |
332 | int fromindex, endfrom, toindex; | |
333 | ||
334 | setprof(kvp, GMON_PROF_OFF); | |
335 | fp = fopen("gmon.out", "w"); | |
336 | if (fp == 0) { | |
337 | perror("gmon.out"); | |
338 | return; | |
339 | } | |
340 | ||
341 | /* | |
342 | * Build the gmon header and write it to a file. | |
343 | */ | |
344 | bzero(&h, sizeof(h)); | |
345 | h.lpc = kvp->gpm.lowpc; | |
346 | h.hpc = kvp->gpm.highpc; | |
347 | h.ncnt = kvp->gpm.kcountsize + sizeof(h); | |
348 | h.version = GMONVERSION; | |
349 | h.profrate = getprofhz(kvp); | |
350 | fwrite((char *)&h, sizeof(h), 1, fp); | |
351 | ||
352 | /* | |
353 | * Write out the tick buffer. | |
354 | */ | |
355 | mib[0] = CTL_KERN; | |
356 | mib[1] = KERN_PROF; | |
357 | if ((tickbuf = (u_short *)malloc(kvp->gpm.kcountsize)) == NULL) { | |
358 | fprintf(stderr, "kgmon: cannot allocate kcount space\n"); | |
359 | exit (5); | |
360 | } | |
361 | if (kflag) { | |
362 | i = kvm_read(kvp->kd, (u_long)kvp->gpm.kcount, (void *)tickbuf, | |
363 | kvp->gpm.kcountsize); | |
364 | } else { | |
365 | mib[2] = GPROF_COUNT; | |
366 | i = kvp->gpm.kcountsize; | |
367 | if (sysctl(mib, 3, tickbuf, &i, NULL, 0) < 0) | |
368 | i = 0; | |
369 | } | |
370 | if (i != kvp->gpm.kcountsize) { | |
371 | (void)fprintf(stderr, "kgmon: read ticks: read %u, got %d: %s", | |
372 | kvp->gpm.kcountsize, i, | |
373 | kflag ? kvm_geterr(kvp->kd) : strerror(errno)); | |
374 | exit(6); | |
375 | } | |
376 | if ((fwrite(tickbuf, kvp->gpm.kcountsize, 1, fp)) != 1) { | |
377 | perror("kgmon: writing tocks to gmon.out"); | |
378 | exit(7); | |
379 | } | |
380 | free(tickbuf); | |
381 | ||
382 | /* | |
383 | * Write out the arc info. | |
384 | */ | |
385 | if ((froms = (u_short *)malloc(kvp->gpm.fromssize)) == NULL) { | |
386 | fprintf(stderr, "kgmon: cannot allocate froms space\n"); | |
387 | exit (8); | |
388 | } | |
389 | if (kflag) { | |
390 | i = kvm_read(kvp->kd, (u_long)kvp->gpm.froms, (void *)froms, | |
391 | kvp->gpm.fromssize); | |
392 | } else { | |
393 | mib[2] = GPROF_FROMS; | |
394 | i = kvp->gpm.fromssize; | |
395 | if (sysctl(mib, 3, froms, &i, NULL, 0) < 0) | |
396 | i = 0; | |
397 | } | |
398 | if (i != kvp->gpm.fromssize) { | |
399 | (void)fprintf(stderr, "kgmon: read froms: read %u, got %d: %s", | |
400 | kvp->gpm.fromssize, i, | |
401 | kflag ? kvm_geterr(kvp->kd) : strerror(errno)); | |
402 | exit(9); | |
403 | } | |
404 | if ((tos = (struct tostruct *)malloc(kvp->gpm.tossize)) == NULL) { | |
405 | fprintf(stderr, "kgmon: cannot allocate tos space\n"); | |
406 | exit(10); | |
407 | } | |
408 | if (kflag) { | |
409 | i = kvm_read(kvp->kd, (u_long)kvp->gpm.tos, (void *)tos, | |
410 | kvp->gpm.tossize); | |
411 | } else { | |
412 | mib[2] = GPROF_TOS; | |
413 | i = kvp->gpm.tossize; | |
414 | if (sysctl(mib, 3, tos, &i, NULL, 0) < 0) | |
415 | i = 0; | |
416 | } | |
417 | if (i != kvp->gpm.tossize) { | |
418 | (void)fprintf(stderr, "kgmon: read tos: read %u, got %d: %s", | |
419 | kvp->gpm.tossize, i, | |
420 | kflag ? kvm_geterr(kvp->kd) : strerror(errno)); | |
421 | exit(11); | |
422 | } | |
423 | if (debug) | |
424 | (void)fprintf(stderr, "kgmon: lowpc 0x%x, textsize 0x%x\n", | |
425 | kvp->gpm.lowpc, kvp->gpm.textsize); | |
426 | endfrom = kvp->gpm.fromssize / sizeof(*froms); | |
427 | for (fromindex = 0; fromindex < endfrom; ++fromindex) { | |
428 | if (froms[fromindex] == 0) | |
429 | continue; | |
430 | frompc = (u_long)kvp->gpm.lowpc + | |
431 | (fromindex * kvp->gpm.hashfraction * sizeof(*froms)); | |
432 | for (toindex = froms[fromindex]; toindex != 0; | |
433 | toindex = tos[toindex].link) { | |
434 | if (debug) | |
435 | (void)fprintf(stderr, | |
436 | "%s: [mcleanup] frompc 0x%x selfpc 0x%x count %d\n", | |
437 | "kgmon", frompc, tos[toindex].selfpc, | |
438 | tos[toindex].count); | |
439 | rawarc.raw_frompc = frompc; | |
440 | rawarc.raw_selfpc = (u_long)tos[toindex].selfpc; | |
441 | rawarc.raw_count = tos[toindex].count; | |
442 | fwrite((char *)&rawarc, sizeof(rawarc), 1, fp); | |
443 | } | |
444 | } | |
445 | fclose(fp); | |
446 | } | |
447 | ||
448 | /* | |
449 | * Get the profiling rate. | |
450 | */ | |
451 | int | |
452 | getprofhz(kvp) | |
453 | struct kvmvars *kvp; | |
454 | { | |
455 | int mib[2], size, profrate; | |
456 | struct clockinfo clockrate; | |
457 | ||
458 | if (kflag) { | |
459 | profrate = 1; | |
460 | if (kvm_read(kvp->kd, nl[N_PROFHZ].n_value, &profrate, | |
461 | sizeof profrate) != sizeof profrate) | |
462 | (void)fprintf(stderr, "kgmon: get clockrate: %s\n", | |
463 | kvm_geterr(kvp->kd)); | |
464 | return (profrate); | |
465 | } | |
466 | mib[0] = CTL_KERN; | |
467 | mib[1] = KERN_CLOCKRATE; | |
468 | clockrate.profhz = 1; | |
469 | size = sizeof clockrate; | |
470 | if (sysctl(mib, 2, &clockrate, &size, NULL, 0) < 0) | |
471 | (void)fprintf(stderr, "kgmon: get clockrate: %s\n", | |
472 | strerror(errno)); | |
473 | return (clockrate.profhz); | |
474 | } | |
475 | ||
476 | /* | |
477 | * Reset the kernel profiling date structures. | |
478 | */ | |
479 | void | |
480 | reset(kvp) | |
481 | struct kvmvars *kvp; | |
482 | { | |
483 | char *zbuf; | |
484 | u_long biggest; | |
485 | int mib[3]; | |
486 | ||
487 | setprof(kvp, GMON_PROF_OFF); | |
488 | ||
489 | biggest = kvp->gpm.kcountsize; | |
490 | if (kvp->gpm.fromssize > biggest) | |
491 | biggest = kvp->gpm.fromssize; | |
492 | if (kvp->gpm.tossize > biggest) | |
493 | biggest = kvp->gpm.tossize; | |
494 | if ((zbuf = (char *)malloc(biggest)) == NULL) { | |
495 | fprintf(stderr, "kgmon: cannot allocate zbuf space\n"); | |
496 | exit(12); | |
497 | } | |
498 | bzero(zbuf, biggest); | |
499 | if (kflag) { | |
500 | if (kvm_write(kvp->kd, (u_long)kvp->gpm.kcount, zbuf, | |
501 | kvp->gpm.kcountsize) != kvp->gpm.kcountsize) { | |
502 | (void)fprintf(stderr, "kgmon: tickbuf zero: %s\n", | |
503 | kvm_geterr(kvp->kd)); | |
504 | exit(13); | |
505 | } | |
506 | if (kvm_write(kvp->kd, (u_long)kvp->gpm.froms, zbuf, | |
507 | kvp->gpm.fromssize) != kvp->gpm.fromssize) { | |
508 | (void)fprintf(stderr, "kgmon: froms zero: %s\n", | |
509 | kvm_geterr(kvp->kd)); | |
510 | exit(14); | |
511 | } | |
512 | if (kvm_write(kvp->kd, (u_long)kvp->gpm.tos, zbuf, | |
513 | kvp->gpm.tossize) != kvp->gpm.tossize) { | |
514 | (void)fprintf(stderr, "kgmon: tos zero: %s\n", | |
515 | kvm_geterr(kvp->kd)); | |
516 | exit(15); | |
517 | } | |
518 | return; | |
519 | } | |
520 | (void)seteuid(0); | |
521 | mib[0] = CTL_KERN; | |
522 | mib[1] = KERN_PROF; | |
523 | mib[2] = GPROF_COUNT; | |
524 | if (sysctl(mib, 3, NULL, NULL, zbuf, kvp->gpm.kcountsize) < 0) { | |
525 | (void)fprintf(stderr, "kgmon: tickbuf zero: %s\n", | |
526 | strerror(errno)); | |
527 | exit(13); | |
528 | } | |
529 | mib[2] = GPROF_FROMS; | |
530 | if (sysctl(mib, 3, NULL, NULL, zbuf, kvp->gpm.fromssize) < 0) { | |
531 | (void)fprintf(stderr, "kgmon: froms zero: %s\n", | |
532 | strerror(errno)); | |
533 | exit(14); | |
534 | } | |
535 | mib[2] = GPROF_TOS; | |
536 | if (sysctl(mib, 3, NULL, NULL, zbuf, kvp->gpm.tossize) < 0) { | |
537 | (void)fprintf(stderr, "kgmon: tos zero: %s\n", | |
538 | strerror(errno)); | |
539 | exit(15); | |
540 | } | |
541 | (void)seteuid(getuid()); | |
542 | free(zbuf); | |
543 | } |