logring.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  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. * logring.c: Implementation of a circular buffer log device
  36. *
  37. * logring makes it easy to access the most recent (and only the most
  38. * recent) output from a process. It works just like "tail -f" on a
  39. * log file, except that the storage required never grows. This can be
  40. * useful in embedded systems where there isn't enough memory or disk
  41. * space for keeping complete log files, but the most recent debugging
  42. * messages are sometimes needed (e.g., after an error is observed).
  43. *
  44. * Logring uses FUSD to implement a character device, /dev/logring,
  45. * that acts like a named pipe that has a finite, circular buffer.
  46. * The size of the buffer is given as a command-line argument. As
  47. * more data is written into the buffer, the oldest data is discarded.
  48. * A process that reads from the logring device will first read the
  49. * existing buffer, then block and see new data as it's written,
  50. * similar to monitoring a log file using "tail -f".
  51. *
  52. * Non-blocking reads are supported; if a process needs to get the
  53. * current contents of the log without blocking to wait for new data,
  54. * it can set the O_NONBLOCK flag when it does the open(), or set it
  55. * later using ioctl().
  56. *
  57. * The select() interface is also supported; programs can select on
  58. * /dev/logring to be notified when new data is available.
  59. *
  60. * Run this example program by typing "logring X", where X is the size
  61. * of the circular buffer in bytes. Then, type "cat /dev/logring" in
  62. * one shell. The cat process will block, waiting for data, similar
  63. * to "tail -f". From another shell, write to the logring (e.g.,
  64. * "echo Hi there > /dev/logring".) The 'cat' process will see the
  65. * message appear.
  66. *
  67. * Note: this example program is based on "emlog", a true Linux kernel
  68. * module with identical functionality. If you find logring useful,
  69. * but want to use it on a system that does not have FUSD, check out
  70. * emlog at http://www.circlemud.org/~jelson/software/emlog.
  71. *
  72. * $Id$
  73. */
  74. #include <stdio.h>
  75. #include <stdlib.h>
  76. #include <string.h>
  77. #include <errno.h>
  78. #include <fcntl.h>
  79. #include <fusd.h>
  80. /* per-client structure to keep track of who has an open FD to us */
  81. struct logring_client {
  82. /* used to store outstanding read and polldiff requests */
  83. struct fusd_file_info *read;
  84. struct fusd_file_info *polldiff;
  85. /* to construct the linked list */
  86. struct logring_client *next;
  87. };
  88. /* list of currently open file descriptors */
  89. struct logring_client *client_list = NULL;
  90. char *logring_data = NULL; /* the data buffer used for the logring */
  91. int logring_size = 0; /* buffer space in the logring */
  92. int logring_writeindex = 0; /* write point in the logring array */
  93. int logring_readindex = 0; /* read point in the logring array */
  94. int logring_offset = 0; /* how far into the total stream is
  95. * logring_read pointing? */
  96. /* amount of data in the queue */
  97. #define LOGRING_QLEN (logring_writeindex >= logring_readindex ? \
  98. logring_writeindex - logring_readindex : \
  99. logring_size - logring_readindex + logring_writeindex)
  100. /* stream byte number of the last byte in the queue */
  101. #define LOGRING_FIRST_EMPTY_BYTE (logring_offset + LOGRING_QLEN)
  102. #define MIN(x, y) ((x) < (y) ? (x) : (y))
  103. /************************************************************************/
  104. /*
  105. * this function removes an element from a linked list. the
  106. * pointer-manipulation insanity below is a trick that prevents the
  107. * "element to be removed is the head of the list" from being a
  108. * special case.
  109. */
  110. void client_list_remove(struct logring_client *c)
  111. {
  112. struct logring_client **ptr;
  113. if (c == NULL || client_list == NULL)
  114. return;
  115. for (ptr = &client_list; *ptr != c; ptr = &((**ptr).next)) {
  116. if (!*ptr) {
  117. fprintf(stderr, "trying to remove a client that isn't in the list\n");
  118. return;
  119. }
  120. }
  121. *ptr = c->next;
  122. }
  123. /* open on /dev/logring: create state for this client */
  124. static int logring_open(struct fusd_file_info *file)
  125. {
  126. /* create state for this client */
  127. struct logring_client *c = malloc(sizeof(struct logring_client));
  128. if (c == NULL)
  129. return -ENOBUFS;
  130. /* initialize fields of this client state */
  131. memset(c, 0, sizeof(struct logring_client));
  132. /* save the pointer to this state so it gets returned to us later */
  133. file->private_data = c;
  134. /* add this client to the client list */
  135. c->next = client_list;
  136. client_list = c;
  137. return 0;
  138. }
  139. /* close on /dev/logring: destroy state for this client */
  140. static int logring_close(struct fusd_file_info *file)
  141. {
  142. struct logring_client *c;
  143. if ((c = (struct logring_client *) file->private_data) != NULL) {
  144. /* take this client off our client list */
  145. client_list_remove(c);
  146. /* if there is a read outstanding, free the state */
  147. if (c->read != NULL) {
  148. fusd_destroy(c->read);
  149. c->read = NULL;
  150. }
  151. /* destroy any outstanding polldiffs */
  152. if (c->polldiff != NULL) {
  153. fusd_destroy(c->polldiff);
  154. c->polldiff = NULL;
  155. }
  156. /* get rid of the struct */
  157. free(c);
  158. file->private_data = NULL;
  159. }
  160. return 0;
  161. }
  162. /*
  163. * This function "completes" a read: that is, matches up a client who
  164. * is requesting data with data that's waiting to be served.
  165. *
  166. * This function is called in two cases:
  167. *
  168. * 1- When a new read request comes in (it might be able to complete
  169. * immediately, if there's data waiting that the client hasn't seen
  170. * yet)
  171. *
  172. * 2- When new data comes in (the new data might be able to complete
  173. * a read that had been previously blocked)
  174. */
  175. void logring_complete_read(struct logring_client *c)
  176. {
  177. loff_t *user_offset;
  178. char *user_buffer;
  179. size_t user_length;
  180. int bytes_copied = 0, n, start_point, retval;
  181. /* if there is no outstanding read, do nothing */
  182. if (c == NULL || c->read == NULL)
  183. return;
  184. /* retrieve the read callback's arguments */
  185. user_offset = fusd_get_offset(c->read);
  186. user_buffer = fusd_get_read_buffer(c->read);
  187. user_length = fusd_get_length(c->read);
  188. /* is the client trying to read data that has scrolled off? */
  189. if (*user_offset < logring_offset)
  190. *user_offset = logring_offset;
  191. /* is there new data this user hasn't seen yet, or are we at EOF? */
  192. /* If we have reached EOF:
  193. * If this is a nonblocking read, return EAGAIN.
  194. * else return without doing anything; keep the read blocked.
  195. */
  196. if (*user_offset >= LOGRING_FIRST_EMPTY_BYTE) {
  197. if (c->read->flags & O_NONBLOCK) {
  198. retval = -EAGAIN;
  199. goto done;
  200. } else {
  201. return;
  202. }
  203. }
  204. /* find the smaller of the total bytes we have available and what
  205. * the user is asking for */
  206. user_length = MIN(user_length, LOGRING_FIRST_EMPTY_BYTE - *user_offset);
  207. retval = user_length;
  208. /* figure out where to start copying data from, based on user's offset */
  209. start_point =
  210. (logring_readindex + (*user_offset-logring_offset)) % logring_size;
  211. /* copy the (possibly noncontiguous) data into user's buffer) */
  212. while (user_length) {
  213. n = MIN(user_length, logring_size - start_point);
  214. memcpy(user_buffer + bytes_copied, logring_data + start_point, n);
  215. bytes_copied += n;
  216. user_length -= n;
  217. start_point = (start_point + n) % logring_size;
  218. }
  219. /* advance the user's file pointer */
  220. *user_offset += retval;
  221. done:
  222. /* and complete the read system call */
  223. fusd_return(c->read, retval);
  224. c->read = NULL;
  225. }
  226. /*
  227. * read on /dev/logring: store the fusd_file_info pointer. then call
  228. * complete_read, which will immediately call fusd_return, if there is
  229. * data already waiting.
  230. *
  231. * Note that this shows a trick we use commonly in FUSD drivers: you
  232. * are allowed to call fusd_return() from within a callback as long as
  233. * you return -FUSD_NOREPLY. In other words, a driver can EITHER
  234. * return a real return value from its callback, OR call fusd_return
  235. * explicitly, but not both.
  236. */
  237. static ssize_t logring_read(struct fusd_file_info *file, char *buffer,
  238. size_t len, loff_t *offset)
  239. {
  240. struct logring_client *c = (struct logring_client *) file->private_data;
  241. if (c == NULL || c->read != NULL) {
  242. fprintf(stderr, "logring_read's arguments are confusd, alas");
  243. return -EINVAL;
  244. }
  245. c->read = file;
  246. logring_complete_read(c);
  247. return -FUSD_NOREPLY;
  248. }
  249. /*
  250. * complete_polldiff: if a client has an outstanding 'polldiff'
  251. * request, possibly return updated poll-state information to the
  252. * kernel, if indeed the state has changed.
  253. */
  254. void logring_complete_polldiff(struct logring_client *c)
  255. {
  256. int curr_state, cached_state;
  257. /* if there is no outstanding polldiff, do nothing */
  258. if (c == NULL || c->polldiff == NULL)
  259. return;
  260. /* figure out the "current" state: i.e. whether or not the logring
  261. * is readable for this client based on its current position in the
  262. * stream. The logring is *always* writable. */
  263. if (*(fusd_get_offset(c->polldiff)) < LOGRING_FIRST_EMPTY_BYTE)
  264. curr_state = FUSD_NOTIFY_INPUT | FUSD_NOTIFY_OUTPUT; /* read and write */
  265. else
  266. curr_state = FUSD_NOTIFY_OUTPUT; /* writable only */
  267. /* cached_state is what the kernel *thinks* the state is */
  268. cached_state = fusd_get_poll_diff_cached_state(c->polldiff);
  269. /* if the state is not what the kernel thinks it is, notify the
  270. kernel of the change */
  271. if (curr_state != cached_state) {
  272. fusd_return(c->polldiff, curr_state);
  273. c->polldiff = NULL;
  274. }
  275. }
  276. /* This function is only called on behalf of clients who are trying to
  277. * use select(). The kernel keeps us up to date on what it thinks the
  278. * current "poll state" is, i.e. readable and/or writable. The kernel
  279. * calls this function every time its assumption about the current
  280. * poll state changes. Every time the driver's notion of the state
  281. * differs from what the kernel thinks it is, it should return the
  282. * poll_diff request with the updated state. Note that a 2nd request
  283. * may come from the kernel before the driver has returned the first
  284. * one; if this happens, use fusd_destroy() to get rid of the older one.
  285. */
  286. int logring_polldiff(struct fusd_file_info *file, unsigned int flags)
  287. {
  288. struct logring_client *c = (struct logring_client *) file->private_data;
  289. if (c == NULL)
  290. return -EIO;
  291. /* if we're already holding a polldiff request that we haven't
  292. * replied to yet, destroy the old one and hold onto only the new
  293. * one */
  294. if (c->polldiff != NULL) {
  295. fusd_destroy(c->polldiff);
  296. c->polldiff = NULL;
  297. }
  298. c->polldiff = file;
  299. logring_complete_polldiff(c);
  300. return -FUSD_NOREPLY;
  301. }
  302. /*
  303. * a write on /dev/logring: first, copy the data from the user into our
  304. * data queue. Then, complete any reads and polldiffs that might be
  305. * outstanding.
  306. */
  307. ssize_t logring_write(struct fusd_file_info *file, const char *buffer,
  308. size_t len, loff_t *offset)
  309. {
  310. struct logring_client *c;
  311. int overflow = 0, bytes_copied = 0, n, retval;
  312. /* if the message is longer than the buffer, just take the beginning
  313. * of it, in hopes that the reader (if any) will have time to read
  314. * before we wrap around and obliterate it */
  315. len = MIN(len, logring_size - 1);
  316. retval = len;
  317. if (len + LOGRING_QLEN >= (logring_size-1)) {
  318. overflow = 1;
  319. /* in case of overflow, figure out where the new buffer will
  320. * begin. we start by figuring out where the current buffer ENDS:
  321. * logring_offset + LOGRING_QLEN. we then advance the end-offset
  322. * by the length of the current write, and work backwards to
  323. * figure out what the oldest unoverwritten data will be (i.e.,
  324. * size of the buffer). was that all quite clear? :-) */
  325. logring_offset = logring_offset + LOGRING_QLEN + len - logring_size + 1;
  326. }
  327. while (len) {
  328. /* how many contiguous bytes are available from the write point to
  329. * the end of the circular buffer? */
  330. n = MIN(len, logring_size - logring_writeindex);
  331. memcpy(logring_data + logring_writeindex, buffer + bytes_copied, n);
  332. bytes_copied += n;
  333. len -= n;
  334. logring_writeindex = (logring_writeindex + n) % logring_size;
  335. }
  336. /* if there was an overflow (i.e., new data wrapped around and
  337. * overwrote old data that had not yet been read), then, reset the
  338. * read point to be whatever the oldest data is that we have. */
  339. if (overflow)
  340. logring_readindex = (logring_writeindex + 1) % logring_size;
  341. /* now, complete any blocked reads and/or polldiffs */
  342. for (c = client_list; c != NULL; c = c->next) {
  343. logring_complete_read(c);
  344. logring_complete_polldiff(c);
  345. }
  346. /* now tell the client how many bytes we acutally wrote */
  347. return retval;
  348. }
  349. int main(int argc, char *argv[])
  350. {
  351. char *name;
  352. /* size must be provided, and an optional logring name */
  353. if (argc != 2 && argc != 3) {
  354. fprintf(stderr, "usage: %s <logring-size> [logring-name]\n", argv[0]);
  355. exit(1);
  356. }
  357. name = (argc == 3 ? argv[2] : "/dev/logring");
  358. /* convert the arg to an int and alloc memory for the logring */
  359. if ((logring_size = atoi(argv[1])) <= 0) {
  360. fprintf(stderr, "invalid logring size; it must be >0\n");
  361. exit(1);
  362. }
  363. if ((logring_data = (char *) malloc(sizeof(char) * logring_size)) == NULL) {
  364. fprintf(stderr, "couldn't allocate %d bytes!\n", logring_size);
  365. exit(1);
  366. }
  367. /* register the fusd device */
  368. fusd_simple_register(name, "misc", "logring", 0666, NULL,
  369. open: logring_open, close: logring_close,
  370. read: logring_read, write: logring_write,
  371. poll_diff: logring_polldiff);
  372. printf("calling fusd_run; reads from /dev/logring will now block\n"
  373. "until someone writes to /dev/logring...\n");
  374. fusd_run();
  375. return 0;
  376. }