The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/kern/kern_hook.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*      $NetBSD: kern_hook.c,v 1.14 2022/10/26 23:21:06 riastradh Exp $ */
    2 
    3 /*-
    4  * Copyright (c) 1997, 1998, 1999, 2002, 2007, 2008 The NetBSD Foundation, Inc.
    5  * All rights reserved.
    6  *
    7  * This code is derived from software contributed to The NetBSD Foundation
    8  * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
    9  * NASA Ames Research Center, and by Luke Mewburn.
   10  *
   11  * Redistribution and use in source and binary forms, with or without
   12  * modification, are permitted provided that the following conditions
   13  * are met:
   14  * 1. Redistributions of source code must retain the above copyright
   15  *    notice, this list of conditions and the following disclaimer.
   16  * 2. Redistributions in binary form must reproduce the above copyright
   17  *    notice, this list of conditions and the following disclaimer in the
   18  *    documentation and/or other materials provided with the distribution.
   19  *
   20  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
   21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
   22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
   23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
   24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   30  * POSSIBILITY OF SUCH DAMAGE.
   31  */
   32 
   33 #include <sys/cdefs.h>
   34 __KERNEL_RCSID(0, "$NetBSD: kern_hook.c,v 1.14 2022/10/26 23:21:06 riastradh Exp $");
   35 
   36 #include <sys/param.h>
   37 
   38 #include <sys/condvar.h>
   39 #include <sys/cpu.h>
   40 #include <sys/device.h>
   41 #include <sys/exec.h>
   42 #include <sys/hook.h>
   43 #include <sys/kmem.h>
   44 #include <sys/malloc.h>
   45 #include <sys/rwlock.h>
   46 #include <sys/systm.h>
   47 
   48 /*
   49  * A generic linear hook.
   50  */
   51 struct hook_desc {
   52         LIST_ENTRY(hook_desc) hk_list;
   53         void    (*hk_fn)(void *);
   54         void    *hk_arg;
   55 };
   56 typedef LIST_HEAD(, hook_desc) hook_list_t;
   57 
   58 enum hook_list_st {
   59         HKLIST_IDLE,
   60         HKLIST_INUSE,
   61 };
   62 
   63 struct khook_list {
   64         hook_list_t      hl_list;
   65         kmutex_t         hl_lock;
   66         kmutex_t        *hl_cvlock;
   67         struct lwp      *hl_lwp;
   68         kcondvar_t       hl_cv;
   69         enum hook_list_st
   70                          hl_state;
   71         khook_t         *hl_active_hk;
   72         char             hl_namebuf[HOOKNAMSIZ];
   73 };
   74 
   75 int     powerhook_debug = 0;
   76 
   77 static void *
   78 hook_establish(hook_list_t *list, void (*fn)(void *), void *arg)
   79 {
   80         struct hook_desc *hd;
   81 
   82         hd = malloc(sizeof(*hd), M_DEVBUF, M_NOWAIT);
   83         if (hd == NULL)
   84                 return (NULL);
   85 
   86         hd->hk_fn = fn;
   87         hd->hk_arg = arg;
   88         LIST_INSERT_HEAD(list, hd, hk_list);
   89 
   90         return (hd);
   91 }
   92 
   93 static void
   94 hook_disestablish(hook_list_t *list, void *vhook)
   95 {
   96 #ifdef DIAGNOSTIC
   97         struct hook_desc *hd;
   98 
   99         LIST_FOREACH(hd, list, hk_list) {
  100                 if (hd == vhook)
  101                         break;
  102         }
  103 
  104         if (hd == NULL)
  105                 panic("hook_disestablish: hook %p not established", vhook);
  106 #endif
  107         LIST_REMOVE((struct hook_desc *)vhook, hk_list);
  108         free(vhook, M_DEVBUF);
  109 }
  110 
  111 static void
  112 hook_destroy(hook_list_t *list)
  113 {
  114         struct hook_desc *hd;
  115 
  116         while ((hd = LIST_FIRST(list)) != NULL) {
  117                 LIST_REMOVE(hd, hk_list);
  118                 free(hd, M_DEVBUF);
  119         }
  120 }
  121 
  122 static void
  123 hook_proc_run(hook_list_t *list, struct proc *p)
  124 {
  125         struct hook_desc *hd;
  126 
  127         LIST_FOREACH(hd, list, hk_list) {
  128                 __FPTRCAST(void (*)(struct proc *, void *), *hd->hk_fn)(p,
  129                     hd->hk_arg);
  130         }
  131 }
  132 
  133 /*
  134  * "Shutdown hook" types, functions, and variables.
  135  *
  136  * Should be invoked immediately before the
  137  * system is halted or rebooted, i.e. after file systems unmounted,
  138  * after crash dump done, etc.
  139  *
  140  * Each shutdown hook is removed from the list before it's run, so that
  141  * it won't be run again.
  142  */
  143 
  144 static hook_list_t shutdownhook_list = LIST_HEAD_INITIALIZER(shutdownhook_list);
  145 
  146 void *
  147 shutdownhook_establish(void (*fn)(void *), void *arg)
  148 {
  149         return hook_establish(&shutdownhook_list, fn, arg);
  150 }
  151 
  152 void
  153 shutdownhook_disestablish(void *vhook)
  154 {
  155         hook_disestablish(&shutdownhook_list, vhook);
  156 }
  157 
  158 /*
  159  * Run shutdown hooks.  Should be invoked immediately before the
  160  * system is halted or rebooted, i.e. after file systems unmounted,
  161  * after crash dump done, etc.
  162  *
  163  * Each shutdown hook is removed from the list before it's run, so that
  164  * it won't be run again.
  165  */
  166 void
  167 doshutdownhooks(void)
  168 {
  169         struct hook_desc *dp;
  170 
  171         while ((dp = LIST_FIRST(&shutdownhook_list)) != NULL) {
  172                 LIST_REMOVE(dp, hk_list);
  173                 (*dp->hk_fn)(dp->hk_arg);
  174 #if 0
  175                 /*
  176                  * Don't bother freeing the hook structure,, since we may
  177                  * be rebooting because of a memory corruption problem,
  178                  * and this might only make things worse.  It doesn't
  179                  * matter, anyway, since the system is just about to
  180                  * reboot.
  181                  */
  182                 free(dp, M_DEVBUF);
  183 #endif
  184         }
  185 }
  186 
  187 /*
  188  * "Mountroot hook" types, functions, and variables.
  189  */
  190 
  191 static hook_list_t mountroothook_list=LIST_HEAD_INITIALIZER(mountroothook_list);
  192 
  193 void *
  194 mountroothook_establish(void (*fn)(device_t), device_t dev)
  195 {
  196         return hook_establish(&mountroothook_list, __FPTRCAST(void (*), fn),
  197             dev);
  198 }
  199 
  200 void
  201 mountroothook_disestablish(void *vhook)
  202 {
  203         hook_disestablish(&mountroothook_list, vhook);
  204 }
  205 
  206 void
  207 mountroothook_destroy(void)
  208 {
  209         hook_destroy(&mountroothook_list);
  210 }
  211 
  212 void
  213 domountroothook(device_t therootdev)
  214 {
  215         struct hook_desc *hd;
  216 
  217         LIST_FOREACH(hd, &mountroothook_list, hk_list) {
  218                 if (hd->hk_arg == therootdev) {
  219                         (*hd->hk_fn)(hd->hk_arg);
  220                         return;
  221                 }
  222         }
  223 }
  224 
  225 static hook_list_t exechook_list = LIST_HEAD_INITIALIZER(exechook_list);
  226 
  227 void *
  228 exechook_establish(void (*fn)(struct proc *, void *), void *arg)
  229 {
  230         return hook_establish(&exechook_list, __FPTRCAST(void (*)(void *), fn),
  231             arg);
  232 }
  233 
  234 void
  235 exechook_disestablish(void *vhook)
  236 {
  237         hook_disestablish(&exechook_list, vhook);
  238 }
  239 
  240 /*
  241  * Run exec hooks.
  242  */
  243 void
  244 doexechooks(struct proc *p)
  245 {
  246         hook_proc_run(&exechook_list, p);
  247 }
  248 
  249 static hook_list_t exithook_list = LIST_HEAD_INITIALIZER(exithook_list);
  250 
  251 void *
  252 exithook_establish(void (*fn)(struct proc *, void *), void *arg)
  253 {
  254         void *rv;
  255 
  256         rw_enter(&exec_lock, RW_WRITER);
  257         rv = hook_establish(&exithook_list, __FPTRCAST(void (*)(void *), fn),
  258             arg);
  259         rw_exit(&exec_lock);
  260         return rv;
  261 }
  262 
  263 void
  264 exithook_disestablish(void *vhook)
  265 {
  266 
  267         rw_enter(&exec_lock, RW_WRITER);
  268         hook_disestablish(&exithook_list, vhook);
  269         rw_exit(&exec_lock);
  270 }
  271 
  272 /*
  273  * Run exit hooks.
  274  */
  275 void
  276 doexithooks(struct proc *p)
  277 {
  278         hook_proc_run(&exithook_list, p);
  279 }
  280 
  281 static hook_list_t forkhook_list = LIST_HEAD_INITIALIZER(forkhook_list);
  282 
  283 void *
  284 forkhook_establish(void (*fn)(struct proc *, struct proc *))
  285 {
  286         return hook_establish(&forkhook_list, __FPTRCAST(void (*)(void *), fn),
  287             NULL);
  288 }
  289 
  290 void
  291 forkhook_disestablish(void *vhook)
  292 {
  293         hook_disestablish(&forkhook_list, vhook);
  294 }
  295 
  296 /*
  297  * Run fork hooks.
  298  */
  299 void
  300 doforkhooks(struct proc *p2, struct proc *p1)
  301 {
  302         struct hook_desc *hd;
  303 
  304         LIST_FOREACH(hd, &forkhook_list, hk_list) {
  305                 __FPTRCAST(void (*)(struct proc *, struct proc *), *hd->hk_fn)
  306                     (p2, p1);
  307         }
  308 }
  309 
  310 static hook_list_t critpollhook_list = LIST_HEAD_INITIALIZER(critpollhook_list);
  311 
  312 void *
  313 critpollhook_establish(void (*fn)(void *), void *arg)
  314 {
  315         return hook_establish(&critpollhook_list, fn, arg);
  316 }
  317 
  318 void
  319 critpollhook_disestablish(void *vhook)
  320 {
  321         hook_disestablish(&critpollhook_list, vhook);
  322 }
  323 
  324 /*
  325  * Run critical polling hooks.
  326  */
  327 void
  328 docritpollhooks(void)
  329 {
  330         struct hook_desc *hd;
  331 
  332         LIST_FOREACH(hd, &critpollhook_list, hk_list) {
  333                 (*hd->hk_fn)(hd->hk_arg);
  334         }
  335 }
  336 
  337 /*
  338  * "Power hook" types, functions, and variables.
  339  * The list of power hooks is kept ordered with the last registered hook
  340  * first.
  341  * When running the hooks on power down the hooks are called in reverse
  342  * registration order, when powering up in registration order.
  343  */
  344 struct powerhook_desc {
  345         TAILQ_ENTRY(powerhook_desc) sfd_list;
  346         void    (*sfd_fn)(int, void *);
  347         void    *sfd_arg;
  348         char    sfd_name[16];
  349 };
  350 
  351 static TAILQ_HEAD(powerhook_head, powerhook_desc) powerhook_list =
  352     TAILQ_HEAD_INITIALIZER(powerhook_list);
  353 
  354 void *
  355 powerhook_establish(const char *name, void (*fn)(int, void *), void *arg)
  356 {
  357         struct powerhook_desc *ndp;
  358 
  359         ndp = (struct powerhook_desc *)
  360             malloc(sizeof(*ndp), M_DEVBUF, M_NOWAIT);
  361         if (ndp == NULL)
  362                 return (NULL);
  363 
  364         ndp->sfd_fn = fn;
  365         ndp->sfd_arg = arg;
  366         strlcpy(ndp->sfd_name, name, sizeof(ndp->sfd_name));
  367         TAILQ_INSERT_HEAD(&powerhook_list, ndp, sfd_list);
  368 
  369         aprint_error("%s: WARNING: powerhook_establish is deprecated\n", name);
  370         return (ndp);
  371 }
  372 
  373 void
  374 powerhook_disestablish(void *vhook)
  375 {
  376 #ifdef DIAGNOSTIC
  377         struct powerhook_desc *dp;
  378 
  379         TAILQ_FOREACH(dp, &powerhook_list, sfd_list)
  380                 if (dp == vhook)
  381                         goto found;
  382         panic("powerhook_disestablish: hook %p not established", vhook);
  383  found:
  384 #endif
  385 
  386         TAILQ_REMOVE(&powerhook_list, (struct powerhook_desc *)vhook,
  387             sfd_list);
  388         free(vhook, M_DEVBUF);
  389 }
  390 
  391 /*
  392  * Run power hooks.
  393  */
  394 void
  395 dopowerhooks(int why)
  396 {
  397         struct powerhook_desc *dp;
  398         const char *why_name;
  399         static const char * pwr_names[] = {PWR_NAMES};
  400         why_name = why < __arraycount(pwr_names) ? pwr_names[why] : "???";
  401 
  402         if (why == PWR_RESUME || why == PWR_SOFTRESUME) {
  403                 TAILQ_FOREACH_REVERSE(dp, &powerhook_list, powerhook_head,
  404                     sfd_list)
  405                 {
  406                         if (powerhook_debug)
  407                                 printf("dopowerhooks %s: %s (%p)\n",
  408                                     why_name, dp->sfd_name, dp);
  409                         (*dp->sfd_fn)(why, dp->sfd_arg);
  410                 }
  411         } else {
  412                 TAILQ_FOREACH(dp, &powerhook_list, sfd_list) {
  413                         if (powerhook_debug)
  414                                 printf("dopowerhooks %s: %s (%p)\n",
  415                                     why_name, dp->sfd_name, dp);
  416                         (*dp->sfd_fn)(why, dp->sfd_arg);
  417                 }
  418         }
  419 
  420         if (powerhook_debug)
  421                 printf("dopowerhooks: %s done\n", why_name);
  422 }
  423 
  424 /*
  425  * A simple linear hook.
  426  */
  427 
  428 khook_list_t *
  429 simplehook_create(int ipl, const char *wmsg)
  430 {
  431         khook_list_t *l;
  432 
  433         l = kmem_zalloc(sizeof(*l), KM_SLEEP);
  434 
  435         mutex_init(&l->hl_lock, MUTEX_DEFAULT, ipl);
  436         strlcpy(l->hl_namebuf, wmsg, sizeof(l->hl_namebuf));
  437         cv_init(&l->hl_cv, l->hl_namebuf);
  438         LIST_INIT(&l->hl_list);
  439         l->hl_state = HKLIST_IDLE;
  440 
  441         return l;
  442 }
  443 
  444 void
  445 simplehook_destroy(khook_list_t *l)
  446 {
  447         struct hook_desc *hd;
  448 
  449         KASSERT(l->hl_state == HKLIST_IDLE);
  450 
  451         while ((hd = LIST_FIRST(&l->hl_list)) != NULL) {
  452                 LIST_REMOVE(hd, hk_list);
  453                 kmem_free(hd, sizeof(*hd));
  454         }
  455 
  456         cv_destroy(&l->hl_cv);
  457         mutex_destroy(&l->hl_lock);
  458         kmem_free(l, sizeof(*l));
  459 }
  460 
  461 int
  462 simplehook_dohooks(khook_list_t *l)
  463 {
  464         struct hook_desc *hd, *nexthd;
  465         kmutex_t *cv_lock;
  466         void (*fn)(void *);
  467         void *arg;
  468 
  469         mutex_enter(&l->hl_lock);
  470         if (l->hl_state != HKLIST_IDLE) {
  471                 mutex_exit(&l->hl_lock);
  472                 return EBUSY;
  473         }
  474 
  475         /* stop removing hooks */
  476         l->hl_state = HKLIST_INUSE;
  477         l->hl_lwp = curlwp;
  478 
  479         LIST_FOREACH(hd, &l->hl_list, hk_list) {
  480                 if (hd->hk_fn == NULL)
  481                         continue;
  482 
  483                 fn = hd->hk_fn;
  484                 arg = hd->hk_arg;
  485                 l->hl_active_hk = hd;
  486                 l->hl_cvlock = NULL;
  487 
  488                 mutex_exit(&l->hl_lock);
  489 
  490                 /* do callback without l->hl_lock */
  491                 (*fn)(arg);
  492 
  493                 mutex_enter(&l->hl_lock);
  494                 l->hl_active_hk = NULL;
  495                 cv_lock = l->hl_cvlock;
  496 
  497                 if (hd->hk_fn == NULL) {
  498                         if (cv_lock != NULL) {
  499                                 mutex_exit(&l->hl_lock);
  500                                 mutex_enter(cv_lock);
  501                         }
  502 
  503                         cv_broadcast(&l->hl_cv);
  504 
  505                         if (cv_lock != NULL) {
  506                                 mutex_exit(cv_lock);
  507                                 mutex_enter(&l->hl_lock);
  508                         }
  509                 }
  510         }
  511 
  512         /* remove marked node while running hooks */
  513         LIST_FOREACH_SAFE(hd, &l->hl_list, hk_list, nexthd) {
  514                 if (hd->hk_fn == NULL) {
  515                         LIST_REMOVE(hd, hk_list);
  516                         kmem_free(hd, sizeof(*hd));
  517                 }
  518         }
  519 
  520         l->hl_lwp = NULL;
  521         l->hl_state = HKLIST_IDLE;
  522         mutex_exit(&l->hl_lock);
  523 
  524         return 0;
  525 }
  526 
  527 khook_t *
  528 simplehook_establish(khook_list_t *l, void (*fn)(void *), void *arg)
  529 {
  530         struct hook_desc *hd;
  531 
  532         hd = kmem_zalloc(sizeof(*hd), KM_SLEEP);
  533         hd->hk_fn = fn;
  534         hd->hk_arg = arg;
  535 
  536         mutex_enter(&l->hl_lock);
  537         LIST_INSERT_HEAD(&l->hl_list, hd, hk_list);
  538         mutex_exit(&l->hl_lock);
  539 
  540         return hd;
  541 }
  542 
  543 void
  544 simplehook_disestablish(khook_list_t *l, khook_t *hd, kmutex_t *lock)
  545 {
  546         struct hook_desc *hd0 __diagused;
  547         kmutex_t *cv_lock;
  548 
  549         KASSERT(lock == NULL || mutex_owned(lock));
  550         mutex_enter(&l->hl_lock);
  551 
  552 #ifdef DIAGNOSTIC
  553         LIST_FOREACH(hd0, &l->hl_list, hk_list) {
  554                 if (hd == hd0)
  555                         break;
  556         }
  557 
  558         if (hd0 == NULL)
  559                 panic("hook_disestablish: hook %p not established", hd);
  560 #endif
  561 
  562         /* The hook is not referred, remove immediately */
  563         if (l->hl_state == HKLIST_IDLE) {
  564                 LIST_REMOVE(hd, hk_list);
  565                 kmem_free(hd, sizeof(*hd));
  566                 mutex_exit(&l->hl_lock);
  567                 return;
  568         }
  569 
  570         /* remove callback. hd will be removed in dohooks */
  571         hd->hk_fn = NULL;
  572         hd->hk_arg = NULL;
  573 
  574         /* If the hook is running, wait for the completion */
  575         if (l->hl_active_hk == hd &&
  576             l->hl_lwp != curlwp) {
  577                 if (lock != NULL) {
  578                         cv_lock = lock;
  579                         KASSERT(l->hl_cvlock == NULL);
  580                         l->hl_cvlock = lock;
  581                         mutex_exit(&l->hl_lock);
  582                 } else {
  583                         cv_lock = &l->hl_lock;
  584                 }
  585 
  586                 cv_wait(&l->hl_cv, cv_lock);
  587 
  588                 if (lock == NULL)
  589                         mutex_exit(&l->hl_lock);
  590         } else {
  591                 mutex_exit(&l->hl_lock);
  592         }
  593 }
  594 
  595 bool
  596 simplehook_has_hooks(khook_list_t *l)
  597 {
  598         bool empty;
  599 
  600         mutex_enter(&l->hl_lock);
  601         empty = LIST_EMPTY(&l->hl_list);
  602         mutex_exit(&l->hl_lock);
  603 
  604         return !empty;
  605 }

Cache object: c771090d3c82220df29fd9616f4d75f4


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.