pager.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. /*
  2. *
  3. * Copyright (c) 2003 The Regents of the University of California. All
  4. * rights reserved.
  5. *
  6. * Redistribution and use in source and binary forms, with or without
  7. * modification, are permitted provided that the following conditions
  8. * are met:
  9. *
  10. * - Redistributions of source code must retain the above copyright
  11. * notice, this list of conditions and the following disclaimer.
  12. *
  13. * - Neither the name of the University nor the names of its
  14. * contributors may be used to endorse or promote products derived
  15. * from this software without specific prior written permission.
  16. *
  17. * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS''
  18. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  19. * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
  20. * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
  21. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  22. * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  23. * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  24. * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
  25. * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  26. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  27. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  28. *
  29. */
  30. /*
  31. * FUSD - The Framework for UserSpace Devices - Example program
  32. *
  33. * Jeremy Elson <jelson@circlemud.org>
  34. *
  35. * pagerd: simple daemon to accept page signals from underlying page
  36. * devices, and redistribute those pages to applications.
  37. *
  38. * The application itself is not especially useful, but this example
  39. * program has proved very valuable as a generic template for FUSD
  40. * drivers that service multiple clients, and implement both blocking
  41. * and selectable devices. This file is a good place to start for
  42. * writing drivers. See logring.c for a more complex real-world
  43. * application based on this template.
  44. *
  45. * How to use the pager:
  46. *
  47. * Interface for devices that generate pages: write "page" to
  48. * /dev/pager/input
  49. *
  50. * Interface for programs waiting for pages: read from (or, select on
  51. * and then read from) /dev/pager/notify. reads will unblock when a
  52. * page arrives. Note that if more than one page arrives before you
  53. * read, you'll only get the most recent one. In other words, you are
  54. * guaranteed to get at least one page.
  55. *
  56. * Important: in order to guarantee that you do not miss any pages,
  57. * you MUST NOT close the file descriptor in between reads/selects.
  58. * If you close the FD and then reopen it, there will be a race (pages
  59. * that arrive between the close and open will not be delivered).
  60. *
  61. * $Id$
  62. */
  63. #include <stdio.h>
  64. #include <stdlib.h>
  65. #include <string.h>
  66. #include <errno.h>
  67. #include <fcntl.h>
  68. #include <fusd.h>
  69. /* EXAMPLE START pager-open.c */
  70. /* per-client structure to keep track of who has an open FD to us */
  71. struct pager_client {
  72. int last_page_seen; /* seq. no. of last page this client has seen */
  73. struct fusd_file_info *read; /* outstanding read request, if any */
  74. struct fusd_file_info *polldiff; /* outstanding polldiff request */
  75. struct pager_client *next; /* to construct the linked list */
  76. };
  77. struct pager_client *client_list = NULL; /* list of clients (open FDs) */
  78. int last_page = 0; /* seq. no. of the most recent page to arrive */
  79. /* EXAMPLE STOP pager-open.c */
  80. void pager_notify_complete_read(struct pager_client *c);
  81. void pager_notify_complete_polldiff(struct pager_client *c);
  82. /************************************************************************/
  83. /*
  84. * this function removes an element from a linked list. the
  85. * pointer-manipulation insanity below is a trick that prevents the
  86. * "element to be removed is the head of the list" from being a
  87. * special case.
  88. */
  89. void client_list_remove(struct pager_client *c)
  90. {
  91. struct pager_client **ptr;
  92. if (c == NULL || client_list == NULL)
  93. return;
  94. for (ptr = &client_list; *ptr != c; ptr = &((**ptr).next)) {
  95. if (!*ptr) {
  96. fprintf(stderr, "trying to remove a client that isn't in the list\n");
  97. return;
  98. }
  99. }
  100. *ptr = c->next;
  101. }
  102. /* EXAMPLE START pager-open.c */
  103. /* open on /dev/pager/notify: create state for this client */
  104. static int pager_notify_open(struct fusd_file_info *file)
  105. {
  106. /* create state for this client */
  107. struct pager_client *c = malloc(sizeof(struct pager_client));
  108. if (c == NULL)
  109. return -ENOBUFS;
  110. /* initialize fields of this client state */
  111. memset(c, 0, sizeof(struct pager_client));
  112. c->last_page_seen = last_page;
  113. /* save the pointer to this state so it gets returned to us later */
  114. file->private_data = c;
  115. /* add this client to the client list */
  116. c->next = client_list;
  117. client_list = c;
  118. return 0;
  119. }
  120. /* EXAMPLE STOP pager-open.c */
  121. /* EXAMPLE START pager-close.c */
  122. /* close on /dev/pager/notify: destroy state for this client */
  123. static int pager_notify_close(struct fusd_file_info *file)
  124. {
  125. struct pager_client *c;
  126. if ((c = (struct pager_client *) file->private_data) != NULL) {
  127. /* take this client off our client list */
  128. client_list_remove(c);
  129. /* if there is a read outstanding, free the state */
  130. if (c->read != NULL) {
  131. fusd_destroy(c->read);
  132. c->read = NULL;
  133. }
  134. /* destroy any outstanding polldiffs */
  135. if (c->polldiff != NULL) {
  136. fusd_destroy(c->polldiff);
  137. c->polldiff = NULL;
  138. }
  139. /* get rid of the struct */
  140. free(c);
  141. file->private_data = NULL;
  142. }
  143. return 0;
  144. }
  145. /* EXAMPLE STOP pager-close.c */
  146. /*
  147. * read on /dev/pager/notify: store the fusd_file_info pointer. then call
  148. * complete_read, which will immediately call fusd_return, if there is
  149. * a page already waiting.
  150. *
  151. * Note that this shows a trick we use commonly in FUSD drivers: you
  152. * are allowed to call fusd_return() from within a callback as long as
  153. * you return -FUSD_NOREPLY. In other words, a driver can EITHER
  154. * return a real return value from its callback, OR call fusd_return
  155. * explicitly, but not both.
  156. */
  157. /* EXAMPLE START pager-read.c */
  158. ssize_t pager_notify_read(struct fusd_file_info *file, char *buffer,
  159. size_t len, loff_t *offset)
  160. {
  161. struct pager_client *c = (struct pager_client *) file->private_data;
  162. if (c == NULL || c->read != NULL) {
  163. fprintf(stderr, "pager_read's arguments are confusd, alas");
  164. return -EINVAL;
  165. }
  166. c->read = file;
  167. pager_notify_complete_read(c);
  168. return -FUSD_NOREPLY;
  169. }
  170. /* EXAMPLE STOP pager-read.c */
  171. /*
  172. * This function "completes" a read: that is, matches up a client who
  173. * is requesting data with data that's waiting to be served.
  174. *
  175. * This function is called in two cases:
  176. *
  177. * 1- When a new read request comes in. The driver might be able to
  178. * complete immediately, if a page arrived between the time the
  179. * process opened the device and performed the read. This is the
  180. * common case for clients that use select. hasn't seen yet - this
  181. * is normal if )
  182. *
  183. * 2- When a new page arrives, all readers are unblocked
  184. */
  185. /* EXAMPLE START pager-read.c */
  186. void pager_notify_complete_read(struct pager_client *c)
  187. {
  188. /* if there is no outstanding read, do nothing */
  189. if (c == NULL || c->read == NULL)
  190. return;
  191. /* if there are no outstanding pages, do nothing */
  192. if (c->last_page_seen >= last_page)
  193. return;
  194. /* bring this client up to date with the most recent page */
  195. c->last_page_seen = last_page;
  196. /* and notify the client by unblocking the read (read returns 0) */
  197. fusd_return(c->read, 0);
  198. c->read = NULL;
  199. }
  200. /* EXAMPLE STOP pager-read.c */
  201. /* This function is only called on behalf of clients who are trying to
  202. * use select(). The kernel keeps us up to date on what it thinks the
  203. * current "poll state" is, i.e. readable and/or writable. The kernel
  204. * calls this function every time its assumption about the current
  205. * poll state changes. Every time the driver's notion of the state
  206. * differs from what the kernel's cached value, it should return the
  207. * poll_diff request with the updated state. Note that a 2nd request
  208. * may come from the kernel before the driver has returned the first
  209. * one; if this happens, use fusd_destroy() to get rid of the older one.
  210. */
  211. /* EXAMPLE START pager-polldiff.c */
  212. ssize_t pager_notify_polldiff(struct fusd_file_info *file,
  213. unsigned int cached_state)
  214. {
  215. struct pager_client *c = (struct pager_client *) file->private_data;
  216. if (c == NULL)
  217. return -EINVAL;
  218. /* if we're already holding a polldiff request that we haven't
  219. * replied to yet, destroy the old one and hold onto only the new
  220. * one */
  221. if (c->polldiff != NULL) {
  222. fusd_destroy(c->polldiff);
  223. c->polldiff = NULL;
  224. }
  225. c->polldiff = file;
  226. pager_notify_complete_polldiff(c);
  227. return -FUSD_NOREPLY;
  228. }
  229. /* EXAMPLE STOP pager-polldiff.c */
  230. /*
  231. * complete_polldiff: if a client has an outstanding 'polldiff'
  232. * request, possibly return updated poll-state information to the
  233. * kernel, if indeed the state has changed.
  234. */
  235. /* EXAMPLE START pager-polldiff.c */
  236. void pager_notify_complete_polldiff(struct pager_client *c)
  237. {
  238. int curr_state, cached_state;
  239. /* if there is no outstanding polldiff, do nothing */
  240. if (c == NULL || c->polldiff == NULL)
  241. return;
  242. /* figure out the "current" state: i.e. whether or not the pager
  243. * is readable for this client based on the last page it saw */
  244. if (c->last_page_seen < last_page)
  245. curr_state = FUSD_NOTIFY_INPUT; /* readable */
  246. else
  247. curr_state = 0; /* not readable or writable */
  248. /* cached_state is what the kernel *thinks* the state is */
  249. cached_state = fusd_get_poll_diff_cached_state(c->polldiff);
  250. /* if the state is not what the kernel thinks it is, notify the
  251. kernel of the change */
  252. if (curr_state != cached_state) {
  253. fusd_return(c->polldiff, curr_state);
  254. c->polldiff = NULL;
  255. }
  256. }
  257. /* EXAMPLE STOP pager-polldiff.c */
  258. /*
  259. * this handles a write on /dev/pager/input. this is called by one of
  260. * the underlying page devices when a page arrives. if a device
  261. * writes "page" to this interface, a page is queued for everyone
  262. * using the notify interface.
  263. */
  264. #define CASE(x) if ((found == 0) && !strcmp(tmp, x) && (found = 1))
  265. /* EXAMPLE START pager-read.c */
  266. ssize_t pager_input_write(struct fusd_file_info *file,
  267. const char *buffer, size_t len, loff_t *offset)
  268. {
  269. struct pager_client *c;
  270. /* ... */
  271. /* EXAMPLE STOP pager-read.c */
  272. char tmp[1024];
  273. int found = 0;
  274. if (len > sizeof(tmp) - 1)
  275. len = sizeof(tmp) - 1;
  276. strncpy(tmp, buffer, len);
  277. tmp[len] = '\0';
  278. /* strip trailing \n's */
  279. while (tmp[len-1] == '\n')
  280. tmp[--len] = '\0';
  281. /* EXAMPLE START pager-read.c */
  282. CASE("page") {
  283. last_page++;
  284. for (c = client_list; c != NULL; c = c->next) {
  285. pager_notify_complete_polldiff(c);
  286. pager_notify_complete_read(c);
  287. }
  288. }
  289. /* EXAMPLE STOP pager-read.c */
  290. /* other commands (if there ever are any) can go here */
  291. if (!found)
  292. return -EINVAL;
  293. else
  294. return len;
  295. }
  296. #undef CASE
  297. static int fusd_success(struct fusd_file_info *file)
  298. {
  299. return 0;
  300. }
  301. int main(int argc, char *argv[])
  302. {
  303. /* register the input device */
  304. fusd_simple_register("/dev/pager/input", "pager", "input", 0666, NULL,
  305. open: fusd_success, close: fusd_success,
  306. write: pager_input_write);
  307. /* register the notification device */
  308. fusd_simple_register("/dev/pager/notify", "pager", "notify", 0666, NULL,
  309. open: pager_notify_open,
  310. close: pager_notify_close,
  311. read: pager_notify_read,
  312. poll_diff: pager_notify_polldiff);
  313. printf("calling fusd_run; reads from /dev/pager/notify will now block\n"
  314. "until someone writes 'page' to /dev/pager/input...\n");
  315. fusd_run();
  316. return 0;
  317. }