1 /* $NetBSD: clockctl.c,v 1.33 2015/12/07 03:25:57 pgoyette 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.33 2015/12/07 03:25:57 pgoyette 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 #ifdef NTP 50 #include <sys/timex.h> 51 #endif /* NTP */ 52 #include <sys/kauth.h> 53 #include <sys/module.h> 54 #include <sys/mutex.h> 55 #include <sys/once.h> 56 57 #include <sys/clockctl.h> 58 #ifdef COMPAT_50 59 #include <compat/sys/clockctl.h> 60 #include <compat/sys/time_types.h> 61 #endif 62 63 kmutex_t clockctl_mtx; 64 int clockctl_refcnt; 65 66 ONCE_DECL(clockctl_once); 67 68 #include "ioconf.h" 69 70 dev_type_ioctl(clockctlioctl); 71 72 const struct cdevsw clockctl_cdevsw = { 73 .d_open = clockctlopen, 74 .d_close = clockctlclose, 75 .d_read = noread, 76 .d_write = nowrite, 77 .d_ioctl = clockctlioctl, 78 .d_stop = nostop, 79 .d_tty = notty, 80 .d_poll = nopoll, 81 .d_mmap = nommap, 82 .d_kqfilter = nokqfilter, 83 .d_discard = nodiscard, 84 .d_flag = D_OTHER, 85 }; 86 87 static kauth_listener_t clockctl_listener; 88 89 static int 90 clockctl_listener_cb(kauth_cred_t cred, kauth_action_t action, void *cookie, 91 void *arg0, void *arg1, void *arg2, void *arg3) 92 { 93 int result; 94 enum kauth_system_req req; 95 bool device_context; 96 97 result = KAUTH_RESULT_DEFER; 98 req = (enum kauth_system_req)arg0; 99 100 if ((action != KAUTH_SYSTEM_TIME) || 101 (req != KAUTH_REQ_SYSTEM_TIME_SYSTEM)) 102 return result; 103 104 device_context = (bool)arg3; 105 106 /* Device is controlled by permissions, so allow. */ 107 if (device_context) 108 result = KAUTH_RESULT_ALLOW; 109 110 return result; 111 } 112 113 /*ARGSUSED*/ 114 void 115 clockctlattach(int num) 116 { 117 118 /* 119 * Don't initialize the listener here - it will get handled as part 120 * of module initialization. 121 */ 122 #if 0 123 clockctl_listener = kauth_listen_scope(KAUTH_SCOPE_SYSTEM, 124 clockctl_listener_cb, NULL); 125 #endif 126 } 127 128 /* 129 * Maintain a refcount for each open/close, so we know when it is 130 * safe to call devsw_detach() 131 */ 132 int 133 clockctlopen(dev_t dev, int flag, int mode, struct lwp *l) 134 { 135 136 mutex_enter(&clockctl_mtx); 137 clockctl_refcnt++; 138 mutex_exit(&clockctl_mtx); 139 140 return 0; 141 } 142 143 int 144 clockctlclose(dev_t dev, int flag, int mode, struct lwp *l) 145 { 146 147 mutex_enter(&clockctl_mtx); 148 clockctl_refcnt--; 149 mutex_exit(&clockctl_mtx); 150 151 return 0; 152 } 153 154 int 155 clockctl_init(void) 156 { 157 158 mutex_init(&clockctl_mtx, MUTEX_DEFAULT, IPL_NONE); 159 clockctl_refcnt = 0; 160 return 0; 161 } 162 163 MODULE(MODULE_CLASS_DRIVER, clockctl, NULL); 164 165 int 166 clockctl_modcmd(modcmd_t cmd, void *data) 167 { 168 int error; 169 #ifdef _MODULE 170 int bmajor, cmajor; 171 #endif 172 173 error = 0; 174 175 switch (cmd) { 176 case MODULE_CMD_INIT: 177 RUN_ONCE(&clockctl_once, clockctl_init); 178 179 clockctl_listener = kauth_listen_scope(KAUTH_SCOPE_SYSTEM, 180 clockctl_listener_cb, NULL); 181 182 #ifdef _MODULE 183 bmajor = cmajor = -1; 184 error = devsw_attach("clockctl", NULL, &bmajor, 185 &clockctl_cdevsw, &cmajor); 186 if (error != 0) 187 kauth_unlisten_scope(clockctl_listener); 188 #endif 189 190 break; 191 192 case MODULE_CMD_FINI: 193 mutex_enter(&clockctl_mtx); 194 if (clockctl_refcnt != 0) { 195 mutex_exit(&clockctl_mtx); 196 return EBUSY; 197 } 198 #ifdef _MODULE 199 error = devsw_detach(NULL, &clockctl_cdevsw); 200 #endif 201 mutex_exit(&clockctl_mtx); 202 203 if (error == 0) 204 kauth_unlisten_scope(clockctl_listener); 205 206 break; 207 208 default: 209 error = ENOTTY; 210 break; 211 } 212 213 return error; 214 } 215 216 int 217 clockctlioctl( 218 dev_t dev, 219 u_long cmd, 220 void *data, 221 int flags, 222 struct lwp *l) 223 { 224 int error = 0; 225 226 switch (cmd) { 227 case CLOCKCTL_SETTIMEOFDAY: { 228 struct clockctl_settimeofday *args = data; 229 230 error = settimeofday1(args->tv, true, args->tzp, l, false); 231 break; 232 } 233 case CLOCKCTL_ADJTIME: { 234 struct timeval atv, oldatv; 235 struct clockctl_adjtime *args = data; 236 237 if (args->delta) { 238 error = copyin(args->delta, &atv, sizeof(atv)); 239 if (error) 240 return (error); 241 } 242 adjtime1(args->delta ? &atv : NULL, 243 args->olddelta ? &oldatv : NULL, l->l_proc); 244 if (args->olddelta) 245 error = copyout(&oldatv, args->olddelta, 246 sizeof(oldatv)); 247 break; 248 } 249 case CLOCKCTL_CLOCK_SETTIME: { 250 struct clockctl_clock_settime *args = data; 251 struct timespec ts; 252 253 error = copyin(args->tp, &ts, sizeof ts); 254 if (error) 255 return (error); 256 error = clock_settime1(l->l_proc, args->clock_id, &ts, false); 257 break; 258 } 259 #ifdef NTP 260 case CLOCKCTL_NTP_ADJTIME: { 261 struct clockctl_ntp_adjtime *args = data; 262 struct timex ntv; 263 264 error = copyin(args->tp, &ntv, sizeof(ntv)); 265 if (error) 266 return (error); 267 268 ntp_adjtime1(&ntv); 269 270 error = copyout(&ntv, args->tp, sizeof(ntv)); 271 if (error == 0) 272 args->retval = ntp_timestatus(); 273 break; 274 } 275 #endif /* NTP */ 276 default: 277 #ifdef COMPAT_50 278 error = compat50_clockctlioctl(dev, cmd, data, flags, l); 279 #else 280 error = EINVAL; 281 #endif 282 } 283 284 return (error); 285 } 286 287 #ifdef COMPAT_50 288 int 289 compat50_clockctlioctl(dev_t dev, u_long cmd, void *data, int flags, 290 struct lwp *l) 291 { 292 int error = 0; 293 const struct cdevsw *cd = cdevsw_lookup(dev); 294 295 if (cd == NULL || cd->d_ioctl == NULL) 296 return ENXIO; 297 298 switch (cmd) { 299 case CLOCKCTL_OSETTIMEOFDAY: { 300 struct timeval50 tv50; 301 struct timeval tv; 302 struct clockctl50_settimeofday *args = data; 303 304 error = copyin(args->tv, &tv50, sizeof(tv50)); 305 if (error) 306 return (error); 307 timeval50_to_timeval(&tv50, &tv); 308 error = settimeofday1(&tv, false, args->tzp, l, false); 309 break; 310 } 311 case CLOCKCTL_OADJTIME: { 312 struct timeval atv, oldatv; 313 struct timeval50 atv50; 314 struct clockctl50_adjtime *args = data; 315 316 if (args->delta) { 317 error = copyin(args->delta, &atv50, sizeof(atv50)); 318 if (error) 319 return (error); 320 timeval50_to_timeval(&atv50, &atv); 321 } 322 adjtime1(args->delta ? &atv : NULL, 323 args->olddelta ? &oldatv : NULL, l->l_proc); 324 if (args->olddelta) { 325 timeval_to_timeval50(&oldatv, &atv50); 326 error = copyout(&atv50, args->olddelta, sizeof(atv50)); 327 } 328 break; 329 } 330 case CLOCKCTL_OCLOCK_SETTIME: { 331 struct timespec50 tp50; 332 struct timespec tp; 333 struct clockctl50_clock_settime *args = data; 334 335 error = copyin(args->tp, &tp50, sizeof(tp50)); 336 if (error) 337 return (error); 338 timespec50_to_timespec(&tp50, &tp); 339 error = clock_settime1(l->l_proc, args->clock_id, &tp, true); 340 break; 341 } 342 case CLOCKCTL_ONTP_ADJTIME: 343 /* The ioctl number changed but the data did not change. */ 344 error = (cd->d_ioctl)(dev, CLOCKCTL_NTP_ADJTIME, 345 data, flags, l); 346 break; 347 default: 348 error = EINVAL; 349 } 350 351 return (error); 352 } 353 #endif 354