kern_time_50.c revision 1.19 1 /* $NetBSD: kern_time_50.c,v 1.19 2011/03/04 01:36:56 christos Exp $ */
2
3 /*-
4 * Copyright (c) 2008, 2009 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Christos Zoulas.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31 #include <sys/cdefs.h>
32 __KERNEL_RCSID(0, "$NetBSD: kern_time_50.c,v 1.19 2011/03/04 01:36:56 christos Exp $");
33
34 #ifdef _KERNEL_OPT
35 #include "opt_aio.h"
36 #include "opt_ntp.h"
37 #include "opt_mqueue.h"
38 #endif
39
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/namei.h>
43 #include <sys/filedesc.h>
44 #include <sys/kernel.h>
45 #include <sys/file.h>
46 #include <sys/stat.h>
47 #include <sys/socketvar.h>
48 #include <sys/vnode.h>
49 #include <sys/proc.h>
50 #include <sys/uio.h>
51 #include <sys/dirent.h>
52 #include <sys/malloc.h>
53 #include <sys/kauth.h>
54 #include <sys/time.h>
55 #include <sys/timex.h>
56 #include <sys/aio.h>
57 #include <sys/poll.h>
58 #include <sys/syscallargs.h>
59 #include <sys/resource.h>
60
61 #include <compat/common/compat_util.h>
62 #include <compat/sys/time.h>
63 #include <compat/sys/timex.h>
64 #include <compat/sys/resource.h>
65 #include <compat/sys/clockctl.h>
66
67 int
68 compat_50_sys_clock_gettime(struct lwp *l,
69 const struct compat_50_sys_clock_gettime_args *uap, register_t *retval)
70 {
71 /* {
72 syscallarg(clockid_t) clock_id;
73 syscallarg(struct timespec50 *) tp;
74 } */
75 int error;
76 struct timespec ats;
77 struct timespec50 ats50;
78
79 error = clock_gettime1(SCARG(uap, clock_id), &ats);
80 if (error != 0)
81 return error;
82
83 timespec_to_timespec50(&ats, &ats50);
84
85 return copyout(&ats50, SCARG(uap, tp), sizeof(ats50));
86 }
87
88 /* ARGSUSED */
89 int
90 compat_50_sys_clock_settime(struct lwp *l,
91 const struct compat_50_sys_clock_settime_args *uap, register_t *retval)
92 {
93 /* {
94 syscallarg(clockid_t) clock_id;
95 syscallarg(const struct timespec50 *) tp;
96 } */
97 int error;
98 struct timespec ats;
99 struct timespec50 ats50;
100
101 error = copyin(SCARG(uap, tp), &ats50, sizeof(ats50));
102 if (error)
103 return error;
104 timespec50_to_timespec(&ats50, &ats);
105
106 return clock_settime1(l->l_proc, SCARG(uap, clock_id), &ats,
107 true);
108 }
109
110
111 int
112 compat_50_sys_clock_getres(struct lwp *l,
113 const struct compat_50_sys_clock_getres_args *uap, register_t *retval)
114 {
115 /* {
116 syscallarg(clockid_t) clock_id;
117 syscallarg(struct timespec50 *) tp;
118 } */
119 struct timespec50 ats50;
120 struct timespec ats;
121 int error = 0;
122
123 error = clock_getres1(SCARG(uap, clock_id), &ats);
124 if (error != 0)
125 return error;
126
127 if (SCARG(uap, tp)) {
128 timespec_to_timespec50(&ats, &ats50);
129 error = copyout(&ats50, SCARG(uap, tp), sizeof(ats50));
130 }
131
132 return error;
133 }
134
135 /* ARGSUSED */
136 int
137 compat_50_sys_nanosleep(struct lwp *l,
138 const struct compat_50_sys_nanosleep_args *uap, register_t *retval)
139 {
140 /* {
141 syscallarg(struct timespec50 *) rqtp;
142 syscallarg(struct timespec50 *) rmtp;
143 } */
144 struct timespec rmt, rqt;
145 struct timespec50 rmt50, rqt50;
146 int error, error1;
147
148 error = copyin(SCARG(uap, rqtp), &rqt50, sizeof(rqt50));
149 if (error)
150 return error;
151 timespec50_to_timespec(&rqt50, &rqt);
152
153 error = nanosleep1(l, &rqt, SCARG(uap, rmtp) ? &rmt : NULL);
154 if (SCARG(uap, rmtp) == NULL || (error != 0 && error != EINTR))
155 return error;
156
157 timespec_to_timespec50(&rmt, &rmt50);
158 error1 = copyout(&rmt50, SCARG(uap, rmtp), sizeof(*SCARG(uap, rmtp)));
159 return error1 ? error1 : error;
160 }
161
162 /* ARGSUSED */
163 int
164 compat_50_sys_gettimeofday(struct lwp *l,
165 const struct compat_50_sys_gettimeofday_args *uap, register_t *retval)
166 {
167 /* {
168 syscallarg(struct timeval50 *) tp;
169 syscallarg(void *) tzp; really "struct timezone *";
170 } */
171 struct timeval atv;
172 struct timeval50 atv50;
173 int error = 0;
174 struct timezone tzfake;
175
176 if (SCARG(uap, tp)) {
177 microtime(&atv);
178 timeval_to_timeval50(&atv, &atv50);
179 error = copyout(&atv50, SCARG(uap, tp), sizeof(*SCARG(uap, tp)));
180 if (error)
181 return error;
182 }
183 if (SCARG(uap, tzp)) {
184 /*
185 * NetBSD has no kernel notion of time zone, so we just
186 * fake up a timezone struct and return it if demanded.
187 */
188 tzfake.tz_minuteswest = 0;
189 tzfake.tz_dsttime = 0;
190 error = copyout(&tzfake, SCARG(uap, tzp), sizeof(tzfake));
191 }
192 return error;
193 }
194
195 /* ARGSUSED */
196 int
197 compat_50_sys_settimeofday(struct lwp *l,
198 const struct compat_50_sys_settimeofday_args *uap, register_t *retval)
199 {
200 /* {
201 syscallarg(const struct timeval50 *) tv;
202 syscallarg(const void *) tzp; really "const struct timezone *";
203 } */
204 struct timeval50 atv50;
205 struct timeval atv;
206 int error = copyin(SCARG(uap, tv), &atv50, sizeof(atv50));
207 if (error)
208 return error;
209 timeval50_to_timeval(&atv50, &atv);
210 return settimeofday1(&atv, false, SCARG(uap, tzp), l, true);
211 }
212
213 /* ARGSUSED */
214 int
215 compat_50_sys_adjtime(struct lwp *l,
216 const struct compat_50_sys_adjtime_args *uap, register_t *retval)
217 {
218 /* {
219 syscallarg(const struct timeval50 *) delta;
220 syscallarg(struct timeval50 *) olddelta;
221 } */
222 int error;
223 struct timeval50 delta50, olddelta50;
224 struct timeval delta, olddelta;
225
226 if ((error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_TIME,
227 KAUTH_REQ_SYSTEM_TIME_ADJTIME, NULL, NULL, NULL)) != 0)
228 return error;
229
230 if (SCARG(uap, delta)) {
231 error = copyin(SCARG(uap, delta), &delta50,
232 sizeof(*SCARG(uap, delta)));
233 if (error)
234 return (error);
235 timeval50_to_timeval(&delta50, &delta);
236 }
237 adjtime1(SCARG(uap, delta) ? &delta : NULL,
238 SCARG(uap, olddelta) ? &olddelta : NULL, l->l_proc);
239 if (SCARG(uap, olddelta)) {
240 timeval_to_timeval50(&olddelta, &olddelta50);
241 error = copyout(&olddelta50, SCARG(uap, olddelta),
242 sizeof(*SCARG(uap, olddelta)));
243 }
244 return error;
245 }
246
247 /* BSD routine to set/arm an interval timer. */
248 /* ARGSUSED */
249 int
250 compat_50_sys_getitimer(struct lwp *l,
251 const struct compat_50_sys_getitimer_args *uap, register_t *retval)
252 {
253 /* {
254 syscallarg(int) which;
255 syscallarg(struct itimerval50 *) itv;
256 } */
257 struct proc *p = l->l_proc;
258 struct itimerval aitv;
259 struct itimerval50 aitv50;
260 int error;
261
262 error = dogetitimer(p, SCARG(uap, which), &aitv);
263 if (error)
264 return error;
265 itimerval_to_itimerval50(&aitv, &aitv50);
266 return copyout(&aitv50, SCARG(uap, itv), sizeof(*SCARG(uap, itv)));
267 }
268
269 int
270 compat_50_sys_setitimer(struct lwp *l,
271 const struct compat_50_sys_setitimer_args *uap, register_t *retval)
272 {
273 /* {
274 syscallarg(int) which;
275 syscallarg(const struct itimerval50 *) itv;
276 syscallarg(struct itimerval50 *) oitv;
277 } */
278 struct proc *p = l->l_proc;
279 int which = SCARG(uap, which);
280 struct compat_50_sys_getitimer_args getargs;
281 const struct itimerval50 *itvp;
282 struct itimerval50 aitv50;
283 struct itimerval aitv;
284 int error;
285
286 if ((u_int)which > ITIMER_PROF)
287 return (EINVAL);
288 itvp = SCARG(uap, itv);
289 if (itvp &&
290 (error = copyin(itvp, &aitv50, sizeof(aitv50)) != 0))
291 return (error);
292 itimerval50_to_itimerval(&aitv50, &aitv);
293 if (SCARG(uap, oitv) != NULL) {
294 SCARG(&getargs, which) = which;
295 SCARG(&getargs, itv) = SCARG(uap, oitv);
296 if ((error = compat_50_sys_getitimer(l, &getargs, retval)) != 0)
297 return (error);
298 }
299 if (itvp == 0)
300 return (0);
301
302 return dosetitimer(p, which, &aitv);
303 }
304
305 int
306 compat_50_sys_aio_suspend(struct lwp *l,
307 const struct compat_50_sys_aio_suspend_args *uap, register_t *retval)
308 {
309 /* {
310 syscallarg(const struct aiocb *const[]) list;
311 syscallarg(int) nent;
312 syscallarg(const struct timespec50 *) timeout;
313 } */
314 #ifdef AIO
315 struct aiocb **list;
316 struct timespec ts;
317 struct timespec50 ts50;
318 int error, nent;
319
320 nent = SCARG(uap, nent);
321 if (nent <= 0 || nent > aio_listio_max)
322 return EAGAIN;
323
324 if (SCARG(uap, timeout)) {
325 /* Convert timespec to ticks */
326 error = copyin(SCARG(uap, timeout), &ts50,
327 sizeof(*SCARG(uap, timeout)));
328 if (error)
329 return error;
330 timespec50_to_timespec(&ts50, &ts);
331 }
332 list = kmem_alloc(nent * sizeof(*list), KM_SLEEP);
333 error = copyin(SCARG(uap, list), list, nent * sizeof(*list));
334 if (error)
335 goto out;
336 error = aio_suspend1(l, list, nent, SCARG(uap, timeout) ? &ts : NULL);
337 out:
338 kmem_free(list, nent * sizeof(*list));
339 return error;
340 #else
341 return ENOSYS;
342 #endif
343 }
344
345 int
346 compat_50_sys__lwp_park(struct lwp *l,
347 const struct compat_50_sys__lwp_park_args *uap, register_t *retval)
348 {
349 /* {
350 syscallarg(const struct timespec50 *) ts;
351 syscallarg(lwpid_t) unpark;
352 syscallarg(const void *) hint;
353 syscallarg(const void *) unparkhint;
354 } */
355 struct timespec ts, *tsp;
356 struct timespec50 ts50;
357 int error;
358
359 if (SCARG(uap, ts) == NULL)
360 tsp = NULL;
361 else {
362 error = copyin(SCARG(uap, ts), &ts50, sizeof(ts50));
363 if (error != 0)
364 return error;
365 timespec50_to_timespec(&ts50, &ts);
366 tsp = &ts;
367 }
368
369 if (SCARG(uap, unpark) != 0) {
370 error = lwp_unpark(SCARG(uap, unpark), SCARG(uap, unparkhint));
371 if (error != 0)
372 return error;
373 }
374
375 return lwp_park(tsp, SCARG(uap, hint));
376 }
377
378 int
379 compat_50_sys_mq_timedsend(struct lwp *l,
380 const struct compat_50_sys_mq_timedsend_args *uap, register_t *retval)
381 {
382 /* {
383 syscallarg(mqd_t) mqdes;
384 syscallarg(const char *) msg_ptr;
385 syscallarg(size_t) msg_len;
386 syscallarg(unsigned) msg_prio;
387 syscallarg(const struct timespec50 *) abs_timeout;
388 } */
389 #ifdef MQUEUE
390 struct timespec50 ts50;
391 struct timespec ts, *tsp;
392 int error;
393
394 /* Get and convert time value */
395 if (SCARG(uap, abs_timeout)) {
396 error = copyin(SCARG(uap, abs_timeout), &ts50, sizeof(ts50));
397 if (error)
398 return error;
399 timespec50_to_timespec(&ts50, &ts);
400 tsp = &ts;
401 } else {
402 tsp = NULL;
403 }
404
405 return mq_send1(SCARG(uap, mqdes), SCARG(uap, msg_ptr),
406 SCARG(uap, msg_len), SCARG(uap, msg_prio), tsp);
407 #else
408 return ENOSYS;
409 #endif
410 }
411
412 int
413 compat_50_sys_mq_timedreceive(struct lwp *l,
414 const struct compat_50_sys_mq_timedreceive_args *uap, register_t *retval)
415 {
416 /* {
417 syscallarg(mqd_t) mqdes;
418 syscallarg(char *) msg_ptr;
419 syscallarg(size_t) msg_len;
420 syscallarg(unsigned *) msg_prio;
421 syscallarg(const struct timespec50 *) abs_timeout;
422 } */
423 #ifdef MQUEUE
424 struct timespec ts, *tsp;
425 struct timespec50 ts50;
426 ssize_t mlen;
427 int error;
428
429 /* Get and convert time value */
430 if (SCARG(uap, abs_timeout)) {
431 error = copyin(SCARG(uap, abs_timeout), &ts50, sizeof(ts50));
432 if (error)
433 return error;
434
435 timespec50_to_timespec(&ts50, &ts);
436 tsp = &ts;
437 } else {
438 tsp = NULL;
439 }
440
441 error = mq_recv1(SCARG(uap, mqdes), SCARG(uap, msg_ptr),
442 SCARG(uap, msg_len), SCARG(uap, msg_prio), tsp, &mlen);
443 if (error == 0)
444 *retval = mlen;
445
446 return error;
447 #else
448 return ENOSYS;
449 #endif
450 }
451
452 static int
453 tscopyin(const void *u, void *s, size_t len)
454 {
455 struct timespec50 ts50;
456 int error;
457
458 KASSERT(len == sizeof(struct timespec));
459 error = copyin(u, &ts50, sizeof(ts50));
460 if (error)
461 return error;
462 timespec50_to_timespec(&ts50, s);
463 return 0;
464 }
465
466 static int
467 tscopyout(const void *s, void *u, size_t len)
468 {
469 struct timespec50 ts50;
470
471 KASSERT(len == sizeof(struct timespec));
472 timespec_to_timespec50(s, &ts50);
473 return copyout(&ts50, u, sizeof(ts50));
474 }
475
476 int
477 compat_50_sys___sigtimedwait(struct lwp *l,
478 const struct compat_50_sys___sigtimedwait_args *uap, register_t *retval)
479 {
480 int res;
481
482 res = sigtimedwait1(l,
483 (const struct sys_____sigtimedwait50_args *)uap, retval, copyout,
484 tscopyin, tscopyout);
485 if (!res)
486 *retval = 0; /* XXX NetBSD<=5 was not POSIX compliant */
487 return res;
488 }
489
490 void
491 rusage_to_rusage50(const struct rusage *ru, struct rusage50 *ru50)
492 {
493 (void)memcpy(&ru50->ru_first, &ru->ru_first,
494 (char *)&ru50->ru_last - (char *)&ru50->ru_first +
495 sizeof(ru50->ru_last));
496 ru50->ru_maxrss = ru->ru_maxrss;
497 timeval_to_timeval50(&ru->ru_utime, &ru50->ru_utime);
498 timeval_to_timeval50(&ru->ru_stime, &ru50->ru_stime);
499 }
500
501 int
502 compat_50_sys_getrusage(struct lwp *l,
503 const struct compat_50_sys_getrusage_args *uap, register_t *retval)
504 {
505 /* {
506 syscallarg(int) who;
507 syscallarg(struct rusage50 *) rusage;
508 } */
509 struct rusage ru;
510 struct rusage50 ru50;
511 struct proc *p = l->l_proc;
512
513 switch (SCARG(uap, who)) {
514 case RUSAGE_SELF:
515 mutex_enter(p->p_lock);
516 memcpy(&ru, &p->p_stats->p_ru, sizeof(ru));
517 calcru(p, &ru.ru_utime, &ru.ru_stime, NULL, NULL);
518 mutex_exit(p->p_lock);
519 break;
520
521 case RUSAGE_CHILDREN:
522 mutex_enter(p->p_lock);
523 memcpy(&ru, &p->p_stats->p_cru, sizeof(ru));
524 mutex_exit(p->p_lock);
525 break;
526
527 default:
528 return EINVAL;
529 }
530 rusage_to_rusage50(&ru, &ru50);
531 return copyout(&ru50, SCARG(uap, rusage), sizeof(ru50));
532 }
533
534
535 /* Return the time remaining until a POSIX timer fires. */
536 int
537 compat_50_sys_timer_gettime(struct lwp *l,
538 const struct compat_50_sys_timer_gettime_args *uap, register_t *retval)
539 {
540 /* {
541 syscallarg(timer_t) timerid;
542 syscallarg(struct itimerspec50 *) value;
543 } */
544 struct itimerspec its;
545 struct itimerspec50 its50;
546 int error;
547
548 if ((error = dotimer_gettime(SCARG(uap, timerid), l->l_proc,
549 &its)) != 0)
550 return error;
551 itimerspec_to_itimerspec50(&its, &its50);
552
553 return copyout(&its50, SCARG(uap, value), sizeof(its50));
554 }
555
556 /* Set and arm a POSIX realtime timer */
557 int
558 compat_50_sys_timer_settime(struct lwp *l,
559 const struct compat_50_sys_timer_settime_args *uap, register_t *retval)
560 {
561 /* {
562 syscallarg(timer_t) timerid;
563 syscallarg(int) flags;
564 syscallarg(const struct itimerspec50 *) value;
565 syscallarg(struct itimerspec50 *) ovalue;
566 } */
567 int error;
568 struct itimerspec value, ovalue, *ovp = NULL;
569 struct itimerspec50 value50, ovalue50;
570
571 if ((error = copyin(SCARG(uap, value), &value50, sizeof(value50))) != 0)
572 return error;
573
574 itimerspec50_to_itimerspec(&value50, &value);
575 if (SCARG(uap, ovalue))
576 ovp = &ovalue;
577
578 if ((error = dotimer_settime(SCARG(uap, timerid), &value, ovp,
579 SCARG(uap, flags), l->l_proc)) != 0)
580 return error;
581
582 if (ovp) {
583 itimerspec_to_itimerspec50(&ovalue, &ovalue50);
584 return copyout(&ovalue50, SCARG(uap, ovalue), sizeof(ovalue50));
585 }
586 return 0;
587 }
588
589 /*
590 * ntp_gettime() - NTP user application interface
591 */
592 int
593 compat_50_sys___ntp_gettime30(struct lwp *l,
594 const struct compat_50_sys___ntp_gettime30_args *uap, register_t *retval)
595 {
596 #ifdef NTP
597 /* {
598 syscallarg(struct ntptimeval *) ntvp;
599 } */
600 struct ntptimeval ntv;
601 struct ntptimeval50 ntv50;
602 int error;
603
604 if (SCARG(uap, ntvp)) {
605 ntp_gettime(&ntv);
606 timespec_to_timespec50(&ntv.time, &ntv50.time);
607 ntv50.maxerror = ntv.maxerror;
608 ntv50.esterror = ntv.esterror;
609 ntv50.tai = ntv.tai;
610 ntv50.time_state = ntv.time_state;
611
612 error = copyout(&ntv50, SCARG(uap, ntvp), sizeof(ntv50));
613 if (error)
614 return error;
615 }
616 *retval = ntp_timestatus();
617 return 0;
618 #else
619 return ENOSYS;
620 #endif
621 }
622 int
623 compat50_clockctlioctl(dev_t dev, u_long cmd, void *data, int flags,
624 struct lwp *l)
625 {
626 int error = 0;
627
628 switch (cmd) {
629 case CLOCKCTL_OSETTIMEOFDAY: {
630 struct timeval50 tv50;
631 struct timeval tv;
632 struct clockctl50_settimeofday *args = data;
633
634 error = copyin(args->tv, &tv50, sizeof(tv50));
635 if (error)
636 return (error);
637 timeval50_to_timeval(&tv50, &tv);
638 error = settimeofday1(&tv, false, args->tzp, l, false);
639 break;
640 }
641 case CLOCKCTL_OADJTIME: {
642 struct timeval atv, oldatv;
643 struct timeval50 atv50;
644 struct clockctl50_adjtime *args = data;
645
646 if (args->delta) {
647 error = copyin(args->delta, &atv50, sizeof(atv50));
648 if (error)
649 return (error);
650 timeval50_to_timeval(&atv50, &atv);
651 }
652 adjtime1(args->delta ? &atv : NULL,
653 args->olddelta ? &oldatv : NULL, l->l_proc);
654 if (args->olddelta) {
655 timeval_to_timeval50(&oldatv, &atv50);
656 error = copyout(&atv50, args->olddelta, sizeof(atv50));
657 }
658 break;
659 }
660 case CLOCKCTL_OCLOCK_SETTIME: {
661 struct timespec50 tp50;
662 struct timespec tp;
663 struct clockctl50_clock_settime *args = data;
664
665 error = copyin(args->tp, &tp50, sizeof(tp50));
666 if (error)
667 return (error);
668 timespec50_to_timespec(&tp50, &tp);
669 error = clock_settime1(l->l_proc, args->clock_id, &tp, true);
670 break;
671 }
672 default:
673 error = EINVAL;
674 }
675
676 return (error);
677 }
678 int
679 compat_50_sys_wait4(struct lwp *l, const struct compat_50_sys_wait4_args *uap,
680 register_t *retval)
681 {
682 /* {
683 syscallarg(int) pid;
684 syscallarg(int *) status;
685 syscallarg(int) options;
686 syscallarg(struct rusage50 *) rusage;
687 } */
688 int status, error, pid = SCARG(uap, pid);
689 struct rusage50 ru50;
690 struct rusage ru;
691
692 error = do_sys_wait(&pid, &status, SCARG(uap, options),
693 SCARG(uap, rusage) != NULL ? &ru : NULL);
694
695 retval[0] = pid;
696 if (pid == 0)
697 return error;
698
699 if (SCARG(uap, rusage)) {
700 rusage_to_rusage50(&ru, &ru50);
701 error = copyout(&ru50, SCARG(uap, rusage), sizeof(ru50));
702 }
703
704 if (error == 0 && SCARG(uap, status))
705 error = copyout(&status, SCARG(uap, status), sizeof(status));
706
707 return error;
708 }
709