1/* $NetBSD: clockctl.c,v 1.39 2022/03/28 12:33:20 riastradh Exp $ */ 2 3/*- 4 * Copyright (c) 2001 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Emmanuel Dreyfus. 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 * 3. The name of the author may not be used to endorse or promote products 19 * derived from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33#include <sys/cdefs.h> 34__KERNEL_RCSID(0, "$NetBSD: clockctl.c,v 1.39 2022/03/28 12:33:20 riastradh Exp $"); 35 36#ifdef _KERNEL_OPT 37#include "opt_ntp.h" 38#include "opt_compat_netbsd.h" 39#endif 40 41#include <sys/param.h> 42#include <sys/systm.h> 43#include <sys/proc.h> 44#include <sys/errno.h> 45#include <sys/ioctl.h> 46#include <sys/device.h> 47#include <sys/time.h> 48#include <sys/conf.h> 49#include <sys/timex.h> 50#include <sys/kauth.h> 51#include <sys/module.h> 52#include <sys/mutex.h> 53#include <sys/compat_stub.h> 54 55#include <sys/clockctl.h> 56#include <compat/sys/clockctl.h> 57#include <compat/sys/time_types.h> 58 59 60kmutex_t clockctl_mtx; 61int clockctl_refcnt; 62 63#include "ioconf.h" 64 65dev_type_ioctl(clockctlioctl); 66 67const struct cdevsw clockctl_cdevsw = { 68 .d_open = clockctlopen, 69 .d_close = clockctlclose, 70 .d_read = noread, 71 .d_write = nowrite, 72 .d_ioctl = clockctlioctl, 73 .d_stop = nostop, 74 .d_tty = notty, 75 .d_poll = nopoll, 76 .d_mmap = nommap, 77 .d_kqfilter = nokqfilter, 78 .d_discard = nodiscard, 79 .d_flag = D_OTHER, 80}; 81 82static kauth_listener_t clockctl_listener; 83 84static int 85clockctl_listener_cb(kauth_cred_t cred, kauth_action_t action, void *cookie, 86 void *arg0, void *arg1, void *arg2, void *arg3) 87{ 88 int result; 89 enum kauth_system_req req; 90 bool device_context; 91 92 result = KAUTH_RESULT_DEFER; 93 req = (enum kauth_system_req)(uintptr_t)arg0; 94 95 if ((action != KAUTH_SYSTEM_TIME) || 96 (req != KAUTH_REQ_SYSTEM_TIME_SYSTEM)) 97 return result; 98 99 device_context = arg3 != NULL; 100 101 /* Device is controlled by permissions, so allow. */ 102 if (device_context) 103 result = KAUTH_RESULT_ALLOW; 104 105 return result; 106} 107 108/*ARGSUSED*/ 109void 110clockctlattach(int num) 111{ 112 113/* 114 * Don't initialize the listener here - it will get handled as part 115 * of module initialization. 116 */ 117#if 0 118 clockctl_listener = kauth_listen_scope(KAUTH_SCOPE_SYSTEM, 119 clockctl_listener_cb, NULL); 120#endif 121} 122 123/* 124 * Maintain a refcount for each open/close, so we know when it is 125 * safe to call devsw_detach() 126 */ 127int 128clockctlopen(dev_t dev, int flag, int mode, struct lwp *l) 129{ 130 131 mutex_enter(&clockctl_mtx); 132 clockctl_refcnt++; 133 mutex_exit(&clockctl_mtx); 134 135 return 0; 136} 137 138int 139clockctlclose(dev_t dev, int flag, int mode, struct lwp *l) 140{ 141 142 mutex_enter(&clockctl_mtx); 143 clockctl_refcnt--; 144 mutex_exit(&clockctl_mtx); 145 146 return 0; 147} 148 149MODULE(MODULE_CLASS_DRIVER, clockctl, NULL); 150 151int 152clockctl_modcmd(modcmd_t cmd, void *data) 153{ 154 int error; 155#ifdef _MODULE 156 int bmajor, cmajor; 157#endif 158 159 error = 0; 160 161 switch (cmd) { 162 case MODULE_CMD_INIT: 163 mutex_init(&clockctl_mtx, MUTEX_DEFAULT, IPL_NONE); 164 165 clockctl_listener = kauth_listen_scope(KAUTH_SCOPE_SYSTEM, 166 clockctl_listener_cb, NULL); 167 168#ifdef _MODULE 169 bmajor = cmajor = -1; 170 error = devsw_attach("clockctl", NULL, &bmajor, 171 &clockctl_cdevsw, &cmajor); 172 if (error != 0) 173 kauth_unlisten_scope(clockctl_listener); 174#endif 175 176 break; 177 178 case MODULE_CMD_FINI: 179 mutex_enter(&clockctl_mtx); 180 if (clockctl_refcnt != 0) { 181 mutex_exit(&clockctl_mtx); 182 return EBUSY; 183 } 184#ifdef _MODULE 185 devsw_detach(NULL, &clockctl_cdevsw); 186#endif 187 mutex_exit(&clockctl_mtx); 188 189 kauth_unlisten_scope(clockctl_listener); 190 mutex_destroy(&clockctl_mtx); 191 break; 192 193 default: 194 error = ENOTTY; 195 break; 196 } 197 198 return error; 199} 200 201int 202clockctlioctl( 203 dev_t dev, 204 u_long cmd, 205 void *data, 206 int flags, 207 struct lwp *l) 208{ 209 int error = 0; 210 211 switch (cmd) { 212 case CLOCKCTL_SETTIMEOFDAY: { 213 struct clockctl_settimeofday *args = data; 214 215 error = settimeofday1(args->tv, true, args->tzp, l, false); 216 break; 217 } 218 case CLOCKCTL_ADJTIME: { 219 struct timeval atv, oldatv; 220 struct clockctl_adjtime *args = data; 221 222 if (args->delta) { 223 error = copyin(args->delta, &atv, sizeof(atv)); 224 if (error) 225 return (error); 226 } 227 adjtime1(args->delta ? &atv : NULL, 228 args->olddelta ? &oldatv : NULL, l->l_proc); 229 if (args->olddelta) 230 error = copyout(&oldatv, args->olddelta, 231 sizeof(oldatv)); 232 break; 233 } 234 case CLOCKCTL_CLOCK_SETTIME: { 235 struct clockctl_clock_settime *args = data; 236 struct timespec ts; 237 238 error = copyin(args->tp, &ts, sizeof ts); 239 if (error) 240 return (error); 241 error = clock_settime1(l->l_proc, args->clock_id, &ts, false); 242 break; 243 } 244 case CLOCKCTL_NTP_ADJTIME: { 245 struct clockctl_ntp_adjtime *args = data; 246 struct timex ntv; 247 248 if (vec_ntp_timestatus == NULL) { 249 error = ENOTTY; 250 break; 251 } 252 error = copyin(args->tp, &ntv, sizeof(ntv)); 253 if (error) 254 return (error); 255 256 (*vec_ntp_adjtime1)(&ntv); 257 258 error = copyout(&ntv, args->tp, sizeof(ntv)); 259 if (error == 0) 260 args->retval = (*vec_ntp_timestatus)(); 261 break; 262 } 263 default: 264 MODULE_HOOK_CALL(clockctl_ioctl_50_hook, 265 (dev, cmd, data, flags, l), enosys(), error); 266 if (error == ENOSYS) 267 error = ENOTTY; 268 } 269 270 return (error); 271} 272