1 | /* $NetBSD: kern_time_50.c,v 1.31 2016/03/11 18:32:29 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.31 2016/03/11 18:32:29 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/conf.h> |
42 | #include <sys/systm.h> |
43 | #include <sys/namei.h> |
44 | #include <sys/filedesc.h> |
45 | #include <sys/kernel.h> |
46 | #include <sys/file.h> |
47 | #include <sys/stat.h> |
48 | #include <sys/socketvar.h> |
49 | #include <sys/vnode.h> |
50 | #include <sys/proc.h> |
51 | #include <sys/uio.h> |
52 | #include <sys/dirent.h> |
53 | #include <sys/kauth.h> |
54 | #include <sys/time.h> |
55 | #include <sys/timex.h> |
56 | #include <sys/clockctl.h> |
57 | #include <sys/aio.h> |
58 | #include <sys/poll.h> |
59 | #include <sys/syscallargs.h> |
60 | #include <sys/sysctl.h> |
61 | #include <sys/resource.h> |
62 | |
63 | #include <compat/common/compat_util.h> |
64 | #include <compat/common/compat_mod.h> |
65 | #include <compat/sys/time.h> |
66 | #include <compat/sys/timex.h> |
67 | #include <compat/sys/resource.h> |
68 | #include <compat/sys/clockctl.h> |
69 | |
70 | struct timeval50 boottime50; |
71 | |
72 | int |
73 | compat_50_sys_clock_gettime(struct lwp *l, |
74 | const struct compat_50_sys_clock_gettime_args *uap, register_t *retval) |
75 | { |
76 | /* { |
77 | syscallarg(clockid_t) clock_id; |
78 | syscallarg(struct timespec50 *) tp; |
79 | } */ |
80 | int error; |
81 | struct timespec ats; |
82 | struct timespec50 ats50; |
83 | |
84 | error = clock_gettime1(SCARG(uap, clock_id), &ats); |
85 | if (error != 0) |
86 | return error; |
87 | |
88 | timespec_to_timespec50(&ats, &ats50); |
89 | |
90 | return copyout(&ats50, SCARG(uap, tp), sizeof(ats50)); |
91 | } |
92 | |
93 | /* ARGSUSED */ |
94 | int |
95 | compat_50_sys_clock_settime(struct lwp *l, |
96 | const struct compat_50_sys_clock_settime_args *uap, register_t *retval) |
97 | { |
98 | /* { |
99 | syscallarg(clockid_t) clock_id; |
100 | syscallarg(const struct timespec50 *) tp; |
101 | } */ |
102 | int error; |
103 | struct timespec ats; |
104 | struct timespec50 ats50; |
105 | |
106 | error = copyin(SCARG(uap, tp), &ats50, sizeof(ats50)); |
107 | if (error) |
108 | return error; |
109 | timespec50_to_timespec(&ats50, &ats); |
110 | |
111 | return clock_settime1(l->l_proc, SCARG(uap, clock_id), &ats, |
112 | true); |
113 | } |
114 | |
115 | |
116 | int |
117 | compat_50_sys_clock_getres(struct lwp *l, |
118 | const struct compat_50_sys_clock_getres_args *uap, register_t *retval) |
119 | { |
120 | /* { |
121 | syscallarg(clockid_t) clock_id; |
122 | syscallarg(struct timespec50 *) tp; |
123 | } */ |
124 | struct timespec50 ats50; |
125 | struct timespec ats; |
126 | int error; |
127 | |
128 | error = clock_getres1(SCARG(uap, clock_id), &ats); |
129 | if (error != 0) |
130 | return error; |
131 | |
132 | if (SCARG(uap, tp)) { |
133 | timespec_to_timespec50(&ats, &ats50); |
134 | error = copyout(&ats50, SCARG(uap, tp), sizeof(ats50)); |
135 | } |
136 | |
137 | return error; |
138 | } |
139 | |
140 | /* ARGSUSED */ |
141 | int |
142 | compat_50_sys_nanosleep(struct lwp *l, |
143 | const struct compat_50_sys_nanosleep_args *uap, register_t *retval) |
144 | { |
145 | /* { |
146 | syscallarg(struct timespec50 *) rqtp; |
147 | syscallarg(struct timespec50 *) rmtp; |
148 | } */ |
149 | struct timespec rmt, rqt; |
150 | struct timespec50 rmt50, rqt50; |
151 | int error, error1; |
152 | |
153 | error = copyin(SCARG(uap, rqtp), &rqt50, sizeof(rqt50)); |
154 | if (error) |
155 | return error; |
156 | timespec50_to_timespec(&rqt50, &rqt); |
157 | |
158 | error = nanosleep1(l, CLOCK_MONOTONIC, 0, &rqt, |
159 | SCARG(uap, rmtp) ? &rmt : NULL); |
160 | if (SCARG(uap, rmtp) == NULL || (error != 0 && error != EINTR)) |
161 | return error; |
162 | |
163 | timespec_to_timespec50(&rmt, &rmt50); |
164 | error1 = copyout(&rmt50, SCARG(uap, rmtp), sizeof(*SCARG(uap, rmtp))); |
165 | return error1 ? error1 : error; |
166 | } |
167 | |
168 | /* ARGSUSED */ |
169 | int |
170 | compat_50_sys_gettimeofday(struct lwp *l, |
171 | const struct compat_50_sys_gettimeofday_args *uap, register_t *retval) |
172 | { |
173 | /* { |
174 | syscallarg(struct timeval50 *) tp; |
175 | syscallarg(void *) tzp; really "struct timezone *"; |
176 | } */ |
177 | struct timeval atv; |
178 | struct timeval50 atv50; |
179 | int error = 0; |
180 | struct timezone tzfake; |
181 | |
182 | if (SCARG(uap, tp)) { |
183 | microtime(&atv); |
184 | timeval_to_timeval50(&atv, &atv50); |
185 | error = copyout(&atv50, SCARG(uap, tp), sizeof(*SCARG(uap, tp))); |
186 | if (error) |
187 | return error; |
188 | } |
189 | if (SCARG(uap, tzp)) { |
190 | /* |
191 | * NetBSD has no kernel notion of time zone, so we just |
192 | * fake up a timezone struct and return it if demanded. |
193 | */ |
194 | tzfake.tz_minuteswest = 0; |
195 | tzfake.tz_dsttime = 0; |
196 | error = copyout(&tzfake, SCARG(uap, tzp), sizeof(tzfake)); |
197 | } |
198 | return error; |
199 | } |
200 | |
201 | /* ARGSUSED */ |
202 | int |
203 | compat_50_sys_settimeofday(struct lwp *l, |
204 | const struct compat_50_sys_settimeofday_args *uap, register_t *retval) |
205 | { |
206 | /* { |
207 | syscallarg(const struct timeval50 *) tv; |
208 | syscallarg(const void *) tzp; really "const struct timezone *"; |
209 | } */ |
210 | struct timeval50 atv50; |
211 | struct timeval atv; |
212 | int error = copyin(SCARG(uap, tv), &atv50, sizeof(atv50)); |
213 | if (error) |
214 | return error; |
215 | timeval50_to_timeval(&atv50, &atv); |
216 | return settimeofday1(&atv, false, SCARG(uap, tzp), l, true); |
217 | } |
218 | |
219 | /* ARGSUSED */ |
220 | int |
221 | compat_50_sys_adjtime(struct lwp *l, |
222 | const struct compat_50_sys_adjtime_args *uap, register_t *retval) |
223 | { |
224 | /* { |
225 | syscallarg(const struct timeval50 *) delta; |
226 | syscallarg(struct timeval50 *) olddelta; |
227 | } */ |
228 | int error; |
229 | struct timeval50 delta50, olddelta50; |
230 | struct timeval delta, olddelta; |
231 | |
232 | if ((error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_TIME, |
233 | KAUTH_REQ_SYSTEM_TIME_ADJTIME, NULL, NULL, NULL)) != 0) |
234 | return error; |
235 | |
236 | if (SCARG(uap, delta)) { |
237 | error = copyin(SCARG(uap, delta), &delta50, |
238 | sizeof(*SCARG(uap, delta))); |
239 | if (error) |
240 | return (error); |
241 | timeval50_to_timeval(&delta50, &delta); |
242 | } |
243 | adjtime1(SCARG(uap, delta) ? &delta : NULL, |
244 | SCARG(uap, olddelta) ? &olddelta : NULL, l->l_proc); |
245 | if (SCARG(uap, olddelta)) { |
246 | timeval_to_timeval50(&olddelta, &olddelta50); |
247 | error = copyout(&olddelta50, SCARG(uap, olddelta), |
248 | sizeof(*SCARG(uap, olddelta))); |
249 | } |
250 | return error; |
251 | } |
252 | |
253 | /* BSD routine to set/arm an interval timer. */ |
254 | /* ARGSUSED */ |
255 | int |
256 | compat_50_sys_getitimer(struct lwp *l, |
257 | const struct compat_50_sys_getitimer_args *uap, register_t *retval) |
258 | { |
259 | /* { |
260 | syscallarg(int) which; |
261 | syscallarg(struct itimerval50 *) itv; |
262 | } */ |
263 | struct proc *p = l->l_proc; |
264 | struct itimerval aitv; |
265 | struct itimerval50 aitv50; |
266 | int error; |
267 | |
268 | error = dogetitimer(p, SCARG(uap, which), &aitv); |
269 | if (error) |
270 | return error; |
271 | itimerval_to_itimerval50(&aitv, &aitv50); |
272 | return copyout(&aitv50, SCARG(uap, itv), sizeof(*SCARG(uap, itv))); |
273 | } |
274 | |
275 | int |
276 | compat_50_sys_setitimer(struct lwp *l, |
277 | const struct compat_50_sys_setitimer_args *uap, register_t *retval) |
278 | { |
279 | /* { |
280 | syscallarg(int) which; |
281 | syscallarg(const struct itimerval50 *) itv; |
282 | syscallarg(struct itimerval50 *) oitv; |
283 | } */ |
284 | struct proc *p = l->l_proc; |
285 | int which = SCARG(uap, which); |
286 | struct compat_50_sys_getitimer_args getargs; |
287 | const struct itimerval50 *itvp; |
288 | struct itimerval50 aitv50; |
289 | struct itimerval aitv; |
290 | int error; |
291 | |
292 | if ((u_int)which > ITIMER_PROF) |
293 | return (EINVAL); |
294 | itvp = SCARG(uap, itv); |
295 | if (itvp && |
296 | (error = copyin(itvp, &aitv50, sizeof(aitv50))) != 0) |
297 | return (error); |
298 | itimerval50_to_itimerval(&aitv50, &aitv); |
299 | if (SCARG(uap, oitv) != NULL) { |
300 | SCARG(&getargs, which) = which; |
301 | SCARG(&getargs, itv) = SCARG(uap, oitv); |
302 | if ((error = compat_50_sys_getitimer(l, &getargs, retval)) != 0) |
303 | return (error); |
304 | } |
305 | if (itvp == 0) |
306 | return (0); |
307 | |
308 | return dosetitimer(p, which, &aitv); |
309 | } |
310 | |
311 | int |
312 | compat_50_sys_aio_suspend(struct lwp *l, |
313 | const struct compat_50_sys_aio_suspend_args *uap, register_t *retval) |
314 | { |
315 | /* { |
316 | syscallarg(const struct aiocb *const[]) list; |
317 | syscallarg(int) nent; |
318 | syscallarg(const struct timespec50 *) timeout; |
319 | } */ |
320 | #ifdef AIO |
321 | struct aiocb **list; |
322 | struct timespec ts; |
323 | struct timespec50 ts50; |
324 | int error, nent; |
325 | |
326 | nent = SCARG(uap, nent); |
327 | if (nent <= 0 || nent > aio_listio_max) |
328 | return EAGAIN; |
329 | |
330 | if (SCARG(uap, timeout)) { |
331 | /* Convert timespec to ticks */ |
332 | error = copyin(SCARG(uap, timeout), &ts50, |
333 | sizeof(*SCARG(uap, timeout))); |
334 | if (error) |
335 | return error; |
336 | timespec50_to_timespec(&ts50, &ts); |
337 | } |
338 | list = kmem_alloc(nent * sizeof(*list), KM_SLEEP); |
339 | error = copyin(SCARG(uap, list), list, nent * sizeof(*list)); |
340 | if (error) |
341 | goto out; |
342 | error = aio_suspend1(l, list, nent, SCARG(uap, timeout) ? &ts : NULL); |
343 | out: |
344 | kmem_free(list, nent * sizeof(*list)); |
345 | return error; |
346 | #else |
347 | return ENOSYS; |
348 | #endif |
349 | } |
350 | |
351 | int |
352 | compat_50_sys_mq_timedsend(struct lwp *l, |
353 | const struct compat_50_sys_mq_timedsend_args *uap, register_t *retval) |
354 | { |
355 | /* { |
356 | syscallarg(mqd_t) mqdes; |
357 | syscallarg(const char *) msg_ptr; |
358 | syscallarg(size_t) msg_len; |
359 | syscallarg(unsigned) msg_prio; |
360 | syscallarg(const struct timespec50 *) abs_timeout; |
361 | } */ |
362 | #ifdef MQUEUE |
363 | struct timespec50 ts50; |
364 | struct timespec ts, *tsp; |
365 | int error; |
366 | |
367 | /* Get and convert time value */ |
368 | if (SCARG(uap, abs_timeout)) { |
369 | error = copyin(SCARG(uap, abs_timeout), &ts50, sizeof(ts50)); |
370 | if (error) |
371 | return error; |
372 | timespec50_to_timespec(&ts50, &ts); |
373 | tsp = &ts; |
374 | } else { |
375 | tsp = NULL; |
376 | } |
377 | |
378 | return mq_send1(SCARG(uap, mqdes), SCARG(uap, msg_ptr), |
379 | SCARG(uap, msg_len), SCARG(uap, msg_prio), tsp); |
380 | #else |
381 | return ENOSYS; |
382 | #endif |
383 | } |
384 | |
385 | int |
386 | compat_50_sys_mq_timedreceive(struct lwp *l, |
387 | const struct compat_50_sys_mq_timedreceive_args *uap, register_t *retval) |
388 | { |
389 | /* { |
390 | syscallarg(mqd_t) mqdes; |
391 | syscallarg(char *) msg_ptr; |
392 | syscallarg(size_t) msg_len; |
393 | syscallarg(unsigned *) msg_prio; |
394 | syscallarg(const struct timespec50 *) abs_timeout; |
395 | } */ |
396 | #ifdef MQUEUE |
397 | struct timespec ts, *tsp; |
398 | struct timespec50 ts50; |
399 | ssize_t mlen; |
400 | int error; |
401 | |
402 | /* Get and convert time value */ |
403 | if (SCARG(uap, abs_timeout)) { |
404 | error = copyin(SCARG(uap, abs_timeout), &ts50, sizeof(ts50)); |
405 | if (error) |
406 | return error; |
407 | |
408 | timespec50_to_timespec(&ts50, &ts); |
409 | tsp = &ts; |
410 | } else { |
411 | tsp = NULL; |
412 | } |
413 | |
414 | error = mq_recv1(SCARG(uap, mqdes), SCARG(uap, msg_ptr), |
415 | SCARG(uap, msg_len), SCARG(uap, msg_prio), tsp, &mlen); |
416 | if (error == 0) |
417 | *retval = mlen; |
418 | |
419 | return error; |
420 | #else |
421 | return ENOSYS; |
422 | #endif |
423 | } |
424 | |
425 | void |
426 | rusage_to_rusage50(const struct rusage *ru, struct rusage50 *ru50) |
427 | { |
428 | (void)memcpy(&ru50->ru_first, &ru->ru_first, |
429 | (char *)&ru50->ru_last - (char *)&ru50->ru_first + |
430 | sizeof(ru50->ru_last)); |
431 | ru50->ru_maxrss = ru->ru_maxrss; |
432 | timeval_to_timeval50(&ru->ru_utime, &ru50->ru_utime); |
433 | timeval_to_timeval50(&ru->ru_stime, &ru50->ru_stime); |
434 | } |
435 | |
436 | int |
437 | compat_50_sys_getrusage(struct lwp *l, |
438 | const struct compat_50_sys_getrusage_args *uap, register_t *retval) |
439 | { |
440 | /* { |
441 | syscallarg(int) who; |
442 | syscallarg(struct rusage50 *) rusage; |
443 | } */ |
444 | int error; |
445 | struct rusage ru; |
446 | struct rusage50 ru50; |
447 | struct proc *p = l->l_proc; |
448 | |
449 | error = getrusage1(p, SCARG(uap, who), &ru); |
450 | if (error != 0) |
451 | return error; |
452 | |
453 | rusage_to_rusage50(&ru, &ru50); |
454 | return copyout(&ru50, SCARG(uap, rusage), sizeof(ru50)); |
455 | } |
456 | |
457 | |
458 | /* Return the time remaining until a POSIX timer fires. */ |
459 | int |
460 | compat_50_sys_timer_gettime(struct lwp *l, |
461 | const struct compat_50_sys_timer_gettime_args *uap, register_t *retval) |
462 | { |
463 | /* { |
464 | syscallarg(timer_t) timerid; |
465 | syscallarg(struct itimerspec50 *) value; |
466 | } */ |
467 | struct itimerspec its; |
468 | struct itimerspec50 its50; |
469 | int error; |
470 | |
471 | if ((error = dotimer_gettime(SCARG(uap, timerid), l->l_proc, |
472 | &its)) != 0) |
473 | return error; |
474 | itimerspec_to_itimerspec50(&its, &its50); |
475 | |
476 | return copyout(&its50, SCARG(uap, value), sizeof(its50)); |
477 | } |
478 | |
479 | /* Set and arm a POSIX realtime timer */ |
480 | int |
481 | compat_50_sys_timer_settime(struct lwp *l, |
482 | const struct compat_50_sys_timer_settime_args *uap, register_t *retval) |
483 | { |
484 | /* { |
485 | syscallarg(timer_t) timerid; |
486 | syscallarg(int) flags; |
487 | syscallarg(const struct itimerspec50 *) value; |
488 | syscallarg(struct itimerspec50 *) ovalue; |
489 | } */ |
490 | int error; |
491 | struct itimerspec value, ovalue, *ovp = NULL; |
492 | struct itimerspec50 value50, ovalue50; |
493 | |
494 | if ((error = copyin(SCARG(uap, value), &value50, sizeof(value50))) != 0) |
495 | return error; |
496 | |
497 | itimerspec50_to_itimerspec(&value50, &value); |
498 | if (SCARG(uap, ovalue)) |
499 | ovp = &ovalue; |
500 | |
501 | if ((error = dotimer_settime(SCARG(uap, timerid), &value, ovp, |
502 | SCARG(uap, flags), l->l_proc)) != 0) |
503 | return error; |
504 | |
505 | if (ovp) { |
506 | itimerspec_to_itimerspec50(&ovalue, &ovalue50); |
507 | return copyout(&ovalue50, SCARG(uap, ovalue), sizeof(ovalue50)); |
508 | } |
509 | return 0; |
510 | } |
511 | |
512 | /* |
513 | * ntp_gettime() - NTP user application interface |
514 | */ |
515 | int |
516 | compat_50_sys___ntp_gettime30(struct lwp *l, |
517 | const struct compat_50_sys___ntp_gettime30_args *uap, register_t *retval) |
518 | { |
519 | #ifdef NTP |
520 | /* { |
521 | syscallarg(struct ntptimeval *) ntvp; |
522 | } */ |
523 | struct ntptimeval ntv; |
524 | struct ntptimeval50 ntv50; |
525 | int error; |
526 | |
527 | if (SCARG(uap, ntvp)) { |
528 | ntp_gettime(&ntv); |
529 | timespec_to_timespec50(&ntv.time, &ntv50.time); |
530 | ntv50.maxerror = ntv.maxerror; |
531 | ntv50.esterror = ntv.esterror; |
532 | ntv50.tai = ntv.tai; |
533 | ntv50.time_state = ntv.time_state; |
534 | |
535 | error = copyout(&ntv50, SCARG(uap, ntvp), sizeof(ntv50)); |
536 | if (error) |
537 | return error; |
538 | } |
539 | *retval = ntp_timestatus(); |
540 | return 0; |
541 | #else |
542 | return ENOSYS; |
543 | #endif |
544 | } |
545 | |
546 | void |
547 | compat_sysctl_time(struct sysctllog **clog) |
548 | { |
549 | struct timeval tv; |
550 | |
551 | TIMESPEC_TO_TIMEVAL(&tv, &boottime); |
552 | timeval_to_timeval50(&tv, &boottime50); |
553 | |
554 | sysctl_createv(clog, 0, NULL, NULL, |
555 | CTLFLAG_PERMANENT, |
556 | CTLTYPE_STRUCT, "oboottime" , |
557 | SYSCTL_DESCR("System boot time" ), |
558 | NULL, 0, &boottime50, sizeof(boottime50), |
559 | CTL_KERN, KERN_OBOOTTIME, CTL_EOL); |
560 | } |
561 | |