direct-io.hg

view tools/xenstore/xenstored_core.c @ 7480:a90d670c98b9

Change the semantics of GetDomainPath so that it always succeeds, regardless of
whether a domain has been introduced to the store. Added a separate message
XS_IS_DOMAIN_INTRODUCED and API for that (xs_is_domain_introduced) to determine
whether the domain has really been introduced. This change means that the
tools can determine the correct domain path earlier in the domain creation
process, which is particularly a factor with live migration, as it allows us
to create the devices earlier in the process, and unpause the new domain before
performing the introduce. Until recently we already had these features, but
the simplification of the interface between xend and xenstored caused breakage.

No longer clear out the domain path when a domain is introduced -- this was a
hack to work around the recent problematic semantics of GetDomainPath.

Do not write the contents of the info block to the store. All the configuration
info is written to the /vm path, and anything else in the info block is either
dealt with explicitly or is ephemeral and has no place in the store.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
author emellor@leeni.uk.xensource.com
date Sun Oct 23 22:45:15 2005 +0100 (2005-10-23)
parents 8dbf531776e1
children 98c6c36ac444
line source
1 /*
2 Simple prototype Xen Store Daemon providing simple tree-like database.
3 Copyright (C) 2005 Rusty Russell IBM Corporation
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <sys/socket.h>
23 #include <sys/select.h>
24 #include <sys/un.h>
25 #include <sys/time.h>
26 #include <time.h>
27 #include <unistd.h>
28 #include <fcntl.h>
29 #include <stdbool.h>
30 #include <stdio.h>
31 #include <stdarg.h>
32 #include <stdlib.h>
33 #include <syslog.h>
34 #include <string.h>
35 #include <errno.h>
36 #include <dirent.h>
37 #include <getopt.h>
38 #include <signal.h>
39 #include <assert.h>
40 #include <setjmp.h>
42 //#define DEBUG
43 #include "utils.h"
44 #include "list.h"
45 #include "talloc.h"
46 #include "xs_lib.h"
47 #include "xenstored_core.h"
48 #include "xenstored_watch.h"
49 #include "xenstored_transaction.h"
50 #include "xenstored_domain.h"
51 #include "xenctrl.h"
52 #include "tdb.h"
54 extern int eventchn_fd; /* in xenstored_domain.c */
56 static bool verbose;
57 LIST_HEAD(connections);
58 static int tracefd = -1;
59 static TDB_CONTEXT *tdb_ctx;
61 #ifdef TESTING
62 static bool failtest = false;
64 /* We override talloc's malloc. */
65 void *test_malloc(size_t size)
66 {
67 /* 1 in 20 means only about 50% of connections establish. */
68 if (failtest && (random() % 32) == 0)
69 return NULL;
70 return malloc(size);
71 }
73 static void stop_failtest(int signum __attribute__((unused)))
74 {
75 failtest = false;
76 }
78 /* Need these before we #define away write_all/mkdir in testing.h */
79 bool test_write_all(int fd, void *contents, unsigned int len);
80 bool test_write_all(int fd, void *contents, unsigned int len)
81 {
82 if (failtest && (random() % 8) == 0) {
83 if (len)
84 len = random() % len;
85 write(fd, contents, len);
86 errno = ENOSPC;
87 return false;
88 }
89 return xs_write_all(fd, contents, len);
90 }
92 int test_mkdir(const char *dir, int perms);
93 int test_mkdir(const char *dir, int perms)
94 {
95 if (failtest && (random() % 8) == 0) {
96 errno = ENOSPC;
97 return -1;
98 }
99 return mkdir(dir, perms);
100 }
101 #endif /* TESTING */
103 #include "xenstored_test.h"
105 /* FIXME: Ideally, this should never be called. Some can be eliminated. */
106 /* Something is horribly wrong: shutdown immediately. */
107 void __attribute__((noreturn)) corrupt(struct connection *conn,
108 const char *fmt, ...)
109 {
110 va_list arglist;
111 char *str;
112 int saved_errno = errno;
114 va_start(arglist, fmt);
115 str = talloc_vasprintf(NULL, fmt, arglist);
116 va_end(arglist);
118 trace("xenstored corruption: connection id %i: err %s: %s",
119 conn ? (int)conn->id : -1, strerror(saved_errno), str);
120 eprintf("xenstored corruption: connection id %i: err %s: %s",
121 conn ? (int)conn->id : -1, strerror(saved_errno), str);
122 #ifdef TESTING
123 /* Allow them to attach debugger. */
124 sleep(30);
125 #endif
126 syslog(LOG_DAEMON,
127 "xenstored corruption: connection id %i: err %s: %s",
128 conn ? (int)conn->id : -1, strerror(saved_errno), str);
129 _exit(2);
130 }
132 TDB_CONTEXT *tdb_context(struct connection *conn)
133 {
134 /* conn = NULL used in manual_node at setup. */
135 if (!conn || !conn->transaction)
136 return tdb_ctx;
137 return tdb_transaction_context(conn->transaction);
138 }
140 bool replace_tdb(const char *newname, TDB_CONTEXT *newtdb)
141 {
142 if (rename(newname, xs_daemon_tdb()) != 0)
143 return false;
144 tdb_close(tdb_ctx);
145 tdb_ctx = talloc_steal(talloc_autofree_context(), newtdb);
146 return true;
147 }
149 static char *sockmsg_string(enum xsd_sockmsg_type type)
150 {
151 switch (type) {
152 case XS_DEBUG: return "DEBUG";
153 case XS_DIRECTORY: return "DIRECTORY";
154 case XS_READ: return "READ";
155 case XS_GET_PERMS: return "GET_PERMS";
156 case XS_WATCH: return "WATCH";
157 case XS_UNWATCH: return "UNWATCH";
158 case XS_TRANSACTION_START: return "TRANSACTION_START";
159 case XS_TRANSACTION_END: return "TRANSACTION_END";
160 case XS_INTRODUCE: return "INTRODUCE";
161 case XS_RELEASE: return "RELEASE";
162 case XS_GET_DOMAIN_PATH: return "GET_DOMAIN_PATH";
163 case XS_WRITE: return "WRITE";
164 case XS_MKDIR: return "MKDIR";
165 case XS_RM: return "RM";
166 case XS_SET_PERMS: return "SET_PERMS";
167 case XS_WATCH_EVENT: return "WATCH_EVENT";
168 case XS_ERROR: return "ERROR";
169 case XS_IS_DOMAIN_INTRODUCED: return "XS_IS_DOMAIN_INTRODUCED";
170 default:
171 return "**UNKNOWN**";
172 }
173 }
175 static void trace_io(const struct connection *conn,
176 const char *prefix,
177 const struct buffered_data *data)
178 {
179 char string[64];
180 unsigned int i;
182 if (tracefd < 0)
183 return;
185 write(tracefd, prefix, strlen(prefix));
186 sprintf(string, " %p ", conn);
187 write(tracefd, string, strlen(string));
188 write(tracefd, sockmsg_string(data->hdr.msg.type),
189 strlen(sockmsg_string(data->hdr.msg.type)));
190 write(tracefd, " (", 2);
191 for (i = 0; i < data->hdr.msg.len; i++) {
192 if (data->buffer[i] == '\0')
193 write(tracefd, " ", 1);
194 else
195 write(tracefd, data->buffer + i, 1);
196 }
197 write(tracefd, ")\n", 2);
198 }
200 void trace_create(const void *data, const char *type)
201 {
202 char string[64];
203 if (tracefd < 0)
204 return;
206 write(tracefd, "CREATE ", strlen("CREATE "));
207 write(tracefd, type, strlen(type));
208 sprintf(string, " %p\n", data);
209 write(tracefd, string, strlen(string));
210 }
212 void trace_destroy(const void *data, const char *type)
213 {
214 char string[64];
215 if (tracefd < 0)
216 return;
218 write(tracefd, "DESTROY ", strlen("DESTROY "));
219 write(tracefd, type, strlen(type));
220 sprintf(string, " %p\n", data);
221 write(tracefd, string, strlen(string));
222 }
224 void trace(const char *fmt, ...)
225 {
226 va_list arglist;
227 char *str;
229 if (tracefd < 0)
230 return;
232 va_start(arglist, fmt);
233 str = talloc_vasprintf(NULL, fmt, arglist);
234 va_end(arglist);
235 write(tracefd, str, strlen(str));
236 talloc_free(str);
237 }
239 static bool write_messages(struct connection *conn)
240 {
241 int ret;
242 struct buffered_data *out;
244 out = list_top(&conn->out_list, struct buffered_data, list);
245 if (out == NULL)
246 return true;
248 if (out->inhdr) {
249 if (verbose)
250 xprintf("Writing msg %s (%.*s) out to %p\n",
251 sockmsg_string(out->hdr.msg.type),
252 out->hdr.msg.len,
253 out->buffer, conn);
254 ret = conn->write(conn, out->hdr.raw + out->used,
255 sizeof(out->hdr) - out->used);
256 if (ret < 0)
257 return false;
259 out->used += ret;
260 if (out->used < sizeof(out->hdr))
261 return true;
263 out->inhdr = false;
264 out->used = 0;
266 /* Second write might block if non-zero. */
267 if (out->hdr.msg.len && !conn->domain)
268 return true;
269 }
271 ret = conn->write(conn, out->buffer + out->used,
272 out->hdr.msg.len - out->used);
273 if (ret < 0)
274 return false;
276 out->used += ret;
277 if (out->used != out->hdr.msg.len)
278 return true;
280 trace_io(conn, "OUT", out);
282 list_del(&out->list);
283 talloc_free(out);
285 return true;
286 }
288 static int destroy_conn(void *_conn)
289 {
290 struct connection *conn = _conn;
292 /* Flush outgoing if possible, but don't block. */
293 if (!conn->domain) {
294 fd_set set;
295 struct timeval none;
297 FD_ZERO(&set);
298 FD_SET(conn->fd, &set);
299 none.tv_sec = none.tv_usec = 0;
301 while (!list_empty(&conn->out_list)
302 && select(conn->fd+1, NULL, &set, NULL, &none) == 1)
303 if (!write_messages(conn))
304 break;
305 close(conn->fd);
306 }
307 list_del(&conn->list);
308 trace_destroy(conn, "connection");
309 return 0;
310 }
312 static int initialize_set(fd_set *inset, fd_set *outset, int sock, int ro_sock)
313 {
314 struct connection *i;
315 int max;
317 FD_ZERO(inset);
318 FD_ZERO(outset);
319 FD_SET(sock, inset);
320 max = sock;
321 FD_SET(ro_sock, inset);
322 if (ro_sock > max)
323 max = ro_sock;
324 FD_SET(eventchn_fd, inset);
325 if (eventchn_fd > max)
326 max = eventchn_fd;
327 list_for_each_entry(i, &connections, list) {
328 if (i->domain)
329 continue;
330 FD_SET(i->fd, inset);
331 if (!list_empty(&i->out_list))
332 FD_SET(i->fd, outset);
333 if (i->fd > max)
334 max = i->fd;
335 }
336 return max;
337 }
339 static int destroy_fd(void *_fd)
340 {
341 int *fd = _fd;
342 close(*fd);
343 return 0;
344 }
346 /* Return a pointer to an fd, self-closing and attached to this pathname. */
347 int *talloc_open(const char *pathname, int flags, int mode)
348 {
349 int *fd;
351 fd = talloc(pathname, int);
352 *fd = open(pathname, flags, mode);
353 if (*fd < 0) {
354 int saved_errno = errno;
355 talloc_free(fd);
356 errno = saved_errno;
357 return NULL;
358 }
359 talloc_set_destructor(fd, destroy_fd);
360 return fd;
361 }
363 /* Is child a subnode of parent, or equal? */
364 bool is_child(const char *child, const char *parent)
365 {
366 unsigned int len = strlen(parent);
368 /* / should really be "" for this algorithm to work, but that's a
369 * usability nightmare. */
370 if (streq(parent, "/"))
371 return true;
373 if (strncmp(child, parent, len) != 0)
374 return false;
376 return child[len] == '/' || child[len] == '\0';
377 }
379 /* If it fails, returns NULL and sets errno. */
380 static struct node *read_node(struct connection *conn, const char *name)
381 {
382 TDB_DATA key, data;
383 uint32_t *p;
384 struct node *node;
386 key.dptr = (void *)name;
387 key.dsize = strlen(name);
388 data = tdb_fetch(tdb_context(conn), key);
390 if (data.dptr == NULL) {
391 if (tdb_error(tdb_context(conn)) == TDB_ERR_NOEXIST)
392 errno = ENOENT;
393 else
394 errno = EIO;
395 return NULL;
396 }
398 node = talloc(name, struct node);
399 node->name = talloc_strdup(node, name);
400 node->parent = NULL;
401 node->tdb = tdb_context(conn);
402 talloc_steal(node, data.dptr);
404 /* Datalen, childlen, number of permissions */
405 p = (uint32_t *)data.dptr;
406 node->num_perms = p[0];
407 node->datalen = p[1];
408 node->childlen = p[2];
410 /* Permissions are struct xs_permissions. */
411 node->perms = (void *)&p[3];
412 /* Data is binary blob (usually ascii, no nul). */
413 node->data = node->perms + node->num_perms;
414 /* Children is strings, nul separated. */
415 node->children = node->data + node->datalen;
417 return node;
418 }
420 static bool write_node(struct connection *conn, const struct node *node)
421 {
422 TDB_DATA key, data;
423 void *p;
425 key.dptr = (void *)node->name;
426 key.dsize = strlen(node->name);
428 data.dsize = 3*sizeof(uint32_t)
429 + node->num_perms*sizeof(node->perms[0])
430 + node->datalen + node->childlen;
431 data.dptr = talloc_size(node, data.dsize);
432 ((uint32_t *)data.dptr)[0] = node->num_perms;
433 ((uint32_t *)data.dptr)[1] = node->datalen;
434 ((uint32_t *)data.dptr)[2] = node->childlen;
435 p = data.dptr + 3 * sizeof(uint32_t);
437 memcpy(p, node->perms, node->num_perms*sizeof(node->perms[0]));
438 p += node->num_perms*sizeof(node->perms[0]);
439 memcpy(p, node->data, node->datalen);
440 p += node->datalen;
441 memcpy(p, node->children, node->childlen);
443 /* TDB should set errno, but doesn't even set ecode AFAICT. */
444 if (tdb_store(tdb_context(conn), key, data, TDB_REPLACE) != 0) {
445 errno = ENOSPC;
446 return false;
447 }
448 return true;
449 }
451 static enum xs_perm_type perm_for_conn(struct connection *conn,
452 struct xs_permissions *perms,
453 unsigned int num)
454 {
455 unsigned int i;
456 enum xs_perm_type mask = XS_PERM_READ|XS_PERM_WRITE|XS_PERM_OWNER;
458 if (!conn->can_write)
459 mask &= ~XS_PERM_WRITE;
461 /* Owners and tools get it all... */
462 if (!conn->id || perms[0].id == conn->id)
463 return (XS_PERM_READ|XS_PERM_WRITE|XS_PERM_OWNER) & mask;
465 for (i = 1; i < num; i++)
466 if (perms[i].id == conn->id)
467 return perms[i].perms & mask;
469 return perms[0].perms & mask;
470 }
472 static char *get_parent(const char *node)
473 {
474 char *slash = strrchr(node + 1, '/');
475 if (!slash)
476 return talloc_strdup(node, "/");
477 return talloc_asprintf(node, "%.*s", (int)(slash - node), node);
478 }
480 /* What do parents say? */
481 static enum xs_perm_type ask_parents(struct connection *conn, const char *name)
482 {
483 struct node *node;
485 do {
486 name = get_parent(name);
487 node = read_node(conn, name);
488 if (node)
489 break;
490 } while (!streq(name, "/"));
492 /* No permission at root? We're in trouble. */
493 if (!node)
494 corrupt(conn, "No permissions file at root");
496 return perm_for_conn(conn, node->perms, node->num_perms);
497 }
499 /* We have a weird permissions system. You can allow someone into a
500 * specific node without allowing it in the parents. If it's going to
501 * fail, however, we don't want the errno to indicate any information
502 * about the node. */
503 static int errno_from_parents(struct connection *conn, const char *node,
504 int errnum, enum xs_perm_type perm)
505 {
506 /* We always tell them about memory failures. */
507 if (errnum == ENOMEM)
508 return errnum;
510 if (ask_parents(conn, node) & perm)
511 return errnum;
512 return EACCES;
513 }
515 /* If it fails, returns NULL and sets errno. */
516 struct node *get_node(struct connection *conn,
517 const char *name,
518 enum xs_perm_type perm)
519 {
520 struct node *node;
522 if (!name || !is_valid_nodename(name)) {
523 errno = EINVAL;
524 return NULL;
525 }
526 node = read_node(conn, name);
527 /* If we don't have permission, we don't have node. */
528 if (node) {
529 if ((perm_for_conn(conn, node->perms, node->num_perms) & perm)
530 != perm)
531 node = NULL;
532 }
533 /* Clean up errno if they weren't supposed to know. */
534 if (!node)
535 errno = errno_from_parents(conn, name, errno, perm);
536 return node;
537 }
539 static struct buffered_data *new_buffer(void *ctx)
540 {
541 struct buffered_data *data;
543 data = talloc(ctx, struct buffered_data);
544 if (data == NULL)
545 return NULL;
547 data->inhdr = true;
548 data->used = 0;
549 data->buffer = NULL;
551 return data;
552 }
554 /* Return length of string (including nul) at this offset. */
555 static unsigned int get_string(const struct buffered_data *data,
556 unsigned int offset)
557 {
558 const char *nul;
560 if (offset >= data->used)
561 return 0;
563 nul = memchr(data->buffer + offset, 0, data->used - offset);
564 if (!nul)
565 return 0;
567 return nul - (data->buffer + offset) + 1;
568 }
570 /* Break input into vectors, return the number, fill in up to num of them. */
571 unsigned int get_strings(struct buffered_data *data,
572 char *vec[], unsigned int num)
573 {
574 unsigned int off, i, len;
576 off = i = 0;
577 while ((len = get_string(data, off)) != 0) {
578 if (i < num)
579 vec[i] = data->buffer + off;
580 i++;
581 off += len;
582 }
583 return i;
584 }
586 void send_reply(struct connection *conn, enum xsd_sockmsg_type type,
587 const void *data, unsigned int len)
588 {
589 struct buffered_data *bdata;
591 /* Message is a child of the connection context for auto-cleanup. */
592 bdata = new_buffer(conn);
593 bdata->buffer = talloc_array(bdata, char, len);
595 /* Echo request header in reply unless this is an async watch event. */
596 if (type != XS_WATCH_EVENT) {
597 memcpy(&bdata->hdr.msg, &conn->in->hdr.msg,
598 sizeof(struct xsd_sockmsg));
599 } else {
600 memset(&bdata->hdr.msg, 0, sizeof(struct xsd_sockmsg));
601 }
603 /* Update relevant header fields and fill in the message body. */
604 bdata->hdr.msg.type = type;
605 bdata->hdr.msg.len = len;
606 memcpy(bdata->buffer, data, len);
608 /* Queue for later transmission. */
609 list_add_tail(&bdata->list, &conn->out_list);
610 }
612 /* Some routines (write, mkdir, etc) just need a non-error return */
613 void send_ack(struct connection *conn, enum xsd_sockmsg_type type)
614 {
615 send_reply(conn, type, "OK", sizeof("OK"));
616 }
618 void send_error(struct connection *conn, int error)
619 {
620 unsigned int i;
622 for (i = 0; error != xsd_errors[i].errnum; i++) {
623 if (i == ARRAY_SIZE(xsd_errors) - 1) {
624 eprintf("xenstored: error %i untranslatable", error);
625 i = 0; /* EINVAL */
626 break;
627 }
628 }
629 send_reply(conn, XS_ERROR, xsd_errors[i].errstring,
630 strlen(xsd_errors[i].errstring) + 1);
631 }
633 static bool valid_chars(const char *node)
634 {
635 /* Nodes can have lots of crap. */
636 return (strspn(node,
637 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
638 "abcdefghijklmnopqrstuvwxyz"
639 "0123456789-/_@") == strlen(node));
640 }
642 bool is_valid_nodename(const char *node)
643 {
644 /* Must start in /. */
645 if (!strstarts(node, "/"))
646 return false;
648 /* Cannot end in / (unless it's just "/"). */
649 if (strends(node, "/") && !streq(node, "/"))
650 return false;
652 /* No double //. */
653 if (strstr(node, "//"))
654 return false;
656 return valid_chars(node);
657 }
659 /* We expect one arg in the input: return NULL otherwise. */
660 static const char *onearg(struct buffered_data *in)
661 {
662 if (!in->used || get_string(in, 0) != in->used)
663 return NULL;
664 return in->buffer;
665 }
667 static char *perms_to_strings(const void *ctx,
668 struct xs_permissions *perms, unsigned int num,
669 unsigned int *len)
670 {
671 unsigned int i;
672 char *strings = NULL;
673 char buffer[MAX_STRLEN(unsigned int) + 1];
675 for (*len = 0, i = 0; i < num; i++) {
676 if (!xs_perm_to_string(&perms[i], buffer))
677 return NULL;
679 strings = talloc_realloc(ctx, strings, char,
680 *len + strlen(buffer) + 1);
681 strcpy(strings + *len, buffer);
682 *len += strlen(buffer) + 1;
683 }
684 return strings;
685 }
687 char *canonicalize(struct connection *conn, const char *node)
688 {
689 const char *prefix;
691 if (!node || strstarts(node, "/"))
692 return (char *)node;
693 prefix = get_implicit_path(conn);
694 if (prefix)
695 return talloc_asprintf(node, "%s/%s", prefix, node);
696 return (char *)node;
697 }
699 bool check_event_node(const char *node)
700 {
701 if (!node || !strstarts(node, "@")) {
702 errno = EINVAL;
703 return false;
704 }
705 return true;
706 }
708 static void send_directory(struct connection *conn, const char *name)
709 {
710 struct node *node;
712 name = canonicalize(conn, name);
713 node = get_node(conn, name, XS_PERM_READ);
714 if (!node) {
715 send_error(conn, errno);
716 return;
717 }
719 send_reply(conn, XS_DIRECTORY, node->children, node->childlen);
720 }
722 static void do_read(struct connection *conn, const char *name)
723 {
724 struct node *node;
726 name = canonicalize(conn, name);
727 node = get_node(conn, name, XS_PERM_READ);
728 if (!node) {
729 send_error(conn, errno);
730 return;
731 }
733 send_reply(conn, XS_READ, node->data, node->datalen);
734 }
736 static void delete_node_single(struct connection *conn, struct node *node)
737 {
738 TDB_DATA key;
740 key.dptr = (void *)node->name;
741 key.dsize = strlen(node->name);
743 if (tdb_delete(tdb_context(conn), key) != 0)
744 corrupt(conn, "Could not delete '%s'", node->name);
745 }
747 /* Must not be / */
748 static char *basename(const char *name)
749 {
750 return strrchr(name, '/') + 1;
751 }
753 static struct node *construct_node(struct connection *conn, const char *name)
754 {
755 const char *base;
756 unsigned int baselen;
757 struct node *parent, *node;
758 char *children, *parentname = get_parent(name);
760 /* If parent doesn't exist, create it. */
761 parent = read_node(conn, parentname);
762 if (!parent)
763 parent = construct_node(conn, parentname);
764 if (!parent)
765 return NULL;
767 /* Add child to parent. */
768 base = basename(name);
769 baselen = strlen(base) + 1;
770 children = talloc_array(name, char, parent->childlen + baselen);
771 memcpy(children, parent->children, parent->childlen);
772 memcpy(children + parent->childlen, base, baselen);
773 parent->children = children;
774 parent->childlen += baselen;
776 /* Allocate node */
777 node = talloc(name, struct node);
778 node->tdb = tdb_context(conn);
779 node->name = talloc_strdup(node, name);
781 /* Inherit permissions, except domains own what they create */
782 node->num_perms = parent->num_perms;
783 node->perms = talloc_memdup(node, parent->perms,
784 node->num_perms * sizeof(node->perms[0]));
785 if (conn->id)
786 node->perms[0].id = conn->id;
788 /* No children, no data */
789 node->children = node->data = NULL;
790 node->childlen = node->datalen = 0;
791 node->parent = parent;
792 return node;
793 }
795 static int destroy_node(void *_node)
796 {
797 struct node *node = _node;
798 TDB_DATA key;
800 if (streq(node->name, "/"))
801 corrupt(NULL, "Destroying root node!");
803 key.dptr = (void *)node->name;
804 key.dsize = strlen(node->name);
806 tdb_delete(node->tdb, key);
807 return 0;
808 }
810 /* Be careful: create heirarchy, put entry in existing parent *last*.
811 * This helps fsck if we die during this. */
812 static struct node *create_node(struct connection *conn,
813 const char *name,
814 void *data, unsigned int datalen)
815 {
816 struct node *node, *i;
818 node = construct_node(conn, name);
819 if (!node)
820 return NULL;
822 node->data = data;
823 node->datalen = datalen;
825 /* We write out the nodes down, setting destructor in case
826 * something goes wrong. */
827 for (i = node; i; i = i->parent) {
828 if (!write_node(conn, i))
829 return NULL;
830 talloc_set_destructor(i, destroy_node);
831 }
833 /* OK, now remove destructors so they stay around */
834 for (i = node; i; i = i->parent)
835 talloc_set_destructor(i, NULL);
836 return node;
837 }
839 /* path, data... */
840 static void do_write(struct connection *conn, struct buffered_data *in)
841 {
842 unsigned int offset, datalen;
843 struct node *node;
844 char *vec[1] = { NULL }; /* gcc4 + -W + -Werror fucks code. */
845 char *name;
847 /* Extra "strings" can be created by binary data. */
848 if (get_strings(in, vec, ARRAY_SIZE(vec)) < ARRAY_SIZE(vec)) {
849 send_error(conn, EINVAL);
850 return;
851 }
853 offset = strlen(vec[0]) + 1;
854 datalen = in->used - offset;
856 name = canonicalize(conn, vec[0]);
857 node = get_node(conn, name, XS_PERM_WRITE);
858 if (!node) {
859 /* No permissions, invalid input? */
860 if (errno != ENOENT) {
861 send_error(conn, errno);
862 return;
863 }
864 node = create_node(conn, name, in->buffer + offset, datalen);
865 if (!node) {
866 send_error(conn, errno);
867 return;
868 }
869 } else {
870 node->data = in->buffer + offset;
871 node->datalen = datalen;
872 if (!write_node(conn, node)){
873 send_error(conn, errno);
874 return;
875 }
876 }
878 add_change_node(conn->transaction, name, false);
879 fire_watches(conn, name, false);
880 send_ack(conn, XS_WRITE);
881 }
883 static void do_mkdir(struct connection *conn, const char *name)
884 {
885 struct node *node;
887 name = canonicalize(conn, name);
888 node = get_node(conn, name, XS_PERM_WRITE);
890 /* If it already exists, fine. */
891 if (!node) {
892 /* No permissions? */
893 if (errno != ENOENT) {
894 send_error(conn, errno);
895 return;
896 }
897 node = create_node(conn, name, NULL, 0);
898 if (!node) {
899 send_error(conn, errno);
900 return;
901 }
902 add_change_node(conn->transaction, name, false);
903 fire_watches(conn, name, false);
904 }
905 send_ack(conn, XS_MKDIR);
906 }
908 static void delete_node(struct connection *conn, struct node *node)
909 {
910 unsigned int i;
912 /* Delete self, then delete children. If something goes wrong,
913 * consistency check will clean up this way. */
914 delete_node_single(conn, node);
916 /* Delete children, too. */
917 for (i = 0; i < node->childlen; i += strlen(node->children+i) + 1) {
918 struct node *child;
920 child = read_node(conn,
921 talloc_asprintf(node, "%s/%s", node->name,
922 node->children + i));
923 if (!child)
924 corrupt(conn, "No child '%s' found", child);
925 delete_node(conn, child);
926 }
927 }
929 /* Delete memory using memmove. */
930 static void memdel(void *mem, unsigned off, unsigned len, unsigned total)
931 {
932 memmove(mem + off, mem + off + len, total - off - len);
933 }
935 static bool delete_child(struct connection *conn,
936 struct node *node, const char *childname)
937 {
938 unsigned int i;
940 for (i = 0; i < node->childlen; i += strlen(node->children+i) + 1) {
941 if (streq(node->children+i, childname)) {
942 memdel(node->children, i, strlen(childname) + 1,
943 node->childlen);
944 node->childlen -= strlen(childname) + 1;
945 return write_node(conn, node);
946 }
947 }
948 corrupt(conn, "Can't find child '%s' in %s", childname, node->name);
949 }
952 static int _rm(struct connection *conn, struct node *node, const char *name)
953 {
954 /* Delete from parent first, then if something explodes fsck cleans. */
955 struct node *parent = read_node(conn, get_parent(name));
956 if (!parent) {
957 send_error(conn, EINVAL);
958 return 0;
959 }
961 if (!delete_child(conn, parent, basename(name))) {
962 send_error(conn, EINVAL);
963 return 0;
964 }
966 delete_node(conn, node);
967 return 1;
968 }
971 static void do_rm(struct connection *conn, const char *name)
972 {
973 struct node *node;
975 name = canonicalize(conn, name);
976 node = get_node(conn, name, XS_PERM_WRITE);
977 if (!node) {
978 /* Didn't exist already? Fine, if parent exists. */
979 if (errno == ENOENT) {
980 node = read_node(conn, get_parent(name));
981 if (node) {
982 send_ack(conn, XS_RM);
983 return;
984 }
985 /* Restore errno, just in case. */
986 errno = ENOENT;
987 }
988 send_error(conn, errno);
989 return;
990 }
992 if (streq(name, "/")) {
993 send_error(conn, EINVAL);
994 return;
995 }
997 if (_rm(conn, node, name)) {
998 add_change_node(conn->transaction, name, true);
999 fire_watches(conn, name, true);
1000 send_ack(conn, XS_RM);
1005 static void do_get_perms(struct connection *conn, const char *name)
1007 struct node *node;
1008 char *strings;
1009 unsigned int len;
1011 name = canonicalize(conn, name);
1012 node = get_node(conn, name, XS_PERM_READ);
1013 if (!node) {
1014 send_error(conn, errno);
1015 return;
1018 strings = perms_to_strings(node, node->perms, node->num_perms, &len);
1019 if (!strings)
1020 send_error(conn, errno);
1021 else
1022 send_reply(conn, XS_GET_PERMS, strings, len);
1025 static void do_set_perms(struct connection *conn, struct buffered_data *in)
1027 unsigned int num;
1028 char *name, *permstr;
1029 struct node *node;
1031 num = xs_count_strings(in->buffer, in->used);
1032 if (num < 2) {
1033 send_error(conn, EINVAL);
1034 return;
1037 /* First arg is node name. */
1038 name = canonicalize(conn, in->buffer);
1039 permstr = in->buffer + strlen(in->buffer) + 1;
1040 num--;
1042 /* We must own node to do this (tools can do this too). */
1043 node = get_node(conn, name, XS_PERM_WRITE|XS_PERM_OWNER);
1044 if (!node) {
1045 send_error(conn, errno);
1046 return;
1049 node->perms = talloc_array(node, struct xs_permissions, num);
1050 node->num_perms = num;
1051 if (!xs_strings_to_perms(node->perms, num, permstr)) {
1052 send_error(conn, errno);
1053 return;
1055 if (!write_node(conn, node)) {
1056 send_error(conn, errno);
1057 return;
1060 add_change_node(conn->transaction, name, false);
1061 fire_watches(conn, name, false);
1062 send_ack(conn, XS_SET_PERMS);
1065 /* Process "in" for conn: "in" will vanish after this conversation, so
1066 * we can talloc off it for temporary variables. May free "conn".
1067 */
1068 static void process_message(struct connection *conn, struct buffered_data *in)
1070 struct transaction *trans;
1072 trans = transaction_lookup(conn, in->hdr.msg.tx_id);
1073 if (IS_ERR(trans)) {
1074 send_error(conn, -PTR_ERR(trans));
1075 return;
1078 assert(conn->transaction == NULL);
1079 conn->transaction = trans;
1081 switch (in->hdr.msg.type) {
1082 case XS_DIRECTORY:
1083 send_directory(conn, onearg(in));
1084 break;
1086 case XS_READ:
1087 do_read(conn, onearg(in));
1088 break;
1090 case XS_WRITE:
1091 do_write(conn, in);
1092 break;
1094 case XS_MKDIR:
1095 do_mkdir(conn, onearg(in));
1096 break;
1098 case XS_RM:
1099 do_rm(conn, onearg(in));
1100 break;
1102 case XS_GET_PERMS:
1103 do_get_perms(conn, onearg(in));
1104 break;
1106 case XS_SET_PERMS:
1107 do_set_perms(conn, in);
1108 break;
1110 case XS_DEBUG:
1111 if (streq(in->buffer, "print"))
1112 xprintf("debug: %s", in->buffer + get_string(in, 0));
1113 #ifdef TESTING
1114 /* For testing, we allow them to set id. */
1115 if (streq(in->buffer, "setid")) {
1116 conn->id = atoi(in->buffer + get_string(in, 0));
1117 send_ack(conn, XS_DEBUG);
1118 } else if (streq(in->buffer, "failtest")) {
1119 if (get_string(in, 0) < in->used)
1120 srandom(atoi(in->buffer + get_string(in, 0)));
1121 send_ack(conn, XS_DEBUG);
1122 failtest = true;
1124 #endif /* TESTING */
1125 break;
1127 case XS_WATCH:
1128 do_watch(conn, in);
1129 break;
1131 case XS_UNWATCH:
1132 do_unwatch(conn, in);
1133 break;
1135 case XS_TRANSACTION_START:
1136 do_transaction_start(conn, in);
1137 break;
1139 case XS_TRANSACTION_END:
1140 do_transaction_end(conn, onearg(in));
1141 break;
1143 case XS_INTRODUCE:
1144 do_introduce(conn, in);
1145 break;
1147 case XS_IS_DOMAIN_INTRODUCED:
1148 do_is_domain_introduced(conn, onearg(in));
1149 break;
1151 case XS_RELEASE:
1152 do_release(conn, onearg(in));
1153 break;
1155 case XS_GET_DOMAIN_PATH:
1156 do_get_domain_path(conn, onearg(in));
1157 break;
1159 default:
1160 eprintf("Client unknown operation %i", in->hdr.msg.type);
1161 send_error(conn, ENOSYS);
1162 break;
1165 conn->transaction = NULL;
1168 static int out_of_mem(void *data)
1170 longjmp(*(jmp_buf *)data, 1);
1173 static void consider_message(struct connection *conn)
1175 jmp_buf talloc_fail;
1177 if (verbose)
1178 xprintf("Got message %s len %i from %p\n",
1179 sockmsg_string(conn->in->hdr.msg.type),
1180 conn->in->hdr.msg.len, conn);
1182 /* For simplicity, we kill the connection on OOM. */
1183 talloc_set_fail_handler(out_of_mem, &talloc_fail);
1184 if (setjmp(talloc_fail)) {
1185 talloc_free(conn);
1186 goto end;
1189 process_message(conn, conn->in);
1191 talloc_free(conn->in);
1192 conn->in = new_buffer(conn);
1194 end:
1195 talloc_set_fail_handler(NULL, NULL);
1196 if (talloc_total_blocks(NULL)
1197 != talloc_total_blocks(talloc_autofree_context()) + 1) {
1198 talloc_report_full(NULL, stderr);
1199 abort();
1203 /* Errors in reading or allocating here mean we get out of sync, so we
1204 * drop the whole client connection. */
1205 static void handle_input(struct connection *conn)
1207 int bytes;
1208 struct buffered_data *in = conn->in;
1210 /* Not finished header yet? */
1211 if (in->inhdr) {
1212 bytes = conn->read(conn, in->hdr.raw + in->used,
1213 sizeof(in->hdr) - in->used);
1214 if (bytes <= 0)
1215 goto bad_client;
1216 in->used += bytes;
1217 if (in->used != sizeof(in->hdr))
1218 return;
1220 if (in->hdr.msg.len > PATH_MAX) {
1221 #ifndef TESTING
1222 syslog(LOG_DAEMON, "Client tried to feed us %i",
1223 in->hdr.msg.len);
1224 #endif
1225 goto bad_client;
1228 in->buffer = talloc_array(in, char, in->hdr.msg.len);
1229 if (!in->buffer)
1230 goto bad_client;
1231 in->used = 0;
1232 in->inhdr = false;
1233 return;
1236 bytes = conn->read(conn, in->buffer + in->used,
1237 in->hdr.msg.len - in->used);
1238 if (bytes < 0)
1239 goto bad_client;
1241 in->used += bytes;
1242 if (in->used != in->hdr.msg.len)
1243 return;
1245 trace_io(conn, "IN ", in);
1246 consider_message(conn);
1247 return;
1249 bad_client:
1250 /* Kill it. */
1251 talloc_free(conn);
1254 static void handle_output(struct connection *conn)
1256 if (!write_messages(conn))
1257 talloc_free(conn);
1260 struct connection *new_connection(connwritefn_t *write, connreadfn_t *read)
1262 struct connection *new;
1264 new = talloc(talloc_autofree_context(), struct connection);
1265 if (!new)
1266 return NULL;
1268 memset(new, 0, sizeof(*new));
1269 new->fd = -1;
1270 new->write = write;
1271 new->read = read;
1272 new->can_write = true;
1273 INIT_LIST_HEAD(&new->out_list);
1274 INIT_LIST_HEAD(&new->watches);
1275 INIT_LIST_HEAD(&new->transaction_list);
1277 new->in = new_buffer(new);
1278 if (new->in == NULL) {
1279 talloc_free(new);
1280 return NULL;
1283 list_add_tail(&new->list, &connections);
1284 talloc_set_destructor(new, destroy_conn);
1285 trace_create(new, "connection");
1286 return new;
1289 static int writefd(struct connection *conn, const void *data, unsigned int len)
1291 return write(conn->fd, data, len);
1294 static int readfd(struct connection *conn, void *data, unsigned int len)
1296 return read(conn->fd, data, len);
1299 static void accept_connection(int sock, bool canwrite)
1301 int fd;
1302 struct connection *conn;
1304 fd = accept(sock, NULL, NULL);
1305 if (fd < 0)
1306 return;
1308 conn = new_connection(writefd, readfd);
1309 if (conn) {
1310 conn->fd = fd;
1311 conn->can_write = canwrite;
1312 } else
1313 close(fd);
1316 #ifdef TESTING
1317 /* Valgrind can check our writes better if we don't use mmap */
1318 #define TDB_FLAGS TDB_NOMMAP
1319 /* Useful for running under debugger. */
1320 void dump_connection(void)
1322 struct connection *i;
1324 list_for_each_entry(i, &connections, list) {
1325 printf("Connection %p:\n", i);
1326 printf(" state = %s\n",
1327 list_empty(&i->out_list) ? "OK" : "BUSY");
1328 if (i->id)
1329 printf(" id = %i\n", i->id);
1330 if (!i->in->inhdr || i->in->used)
1331 printf(" got %i bytes of %s\n",
1332 i->in->used, i->in->inhdr ? "header" : "data");
1333 #if 0
1334 if (i->out)
1335 printf(" sending message %s (%s) out\n",
1336 sockmsg_string(i->out->hdr.msg.type),
1337 i->out->buffer);
1338 if (i->transaction)
1339 dump_transaction(i);
1340 if (i->domain)
1341 dump_domain(i);
1342 #endif
1343 dump_watches(i);
1346 #else
1347 #define TDB_FLAGS 0
1348 #endif
1350 /* We create initial nodes manually. */
1351 static void manual_node(const char *name, const char *child)
1353 struct node *node;
1354 struct xs_permissions perms = { .id = 0, .perms = XS_PERM_READ };
1356 node = talloc(NULL, struct node);
1357 node->name = name;
1358 node->perms = &perms;
1359 node->num_perms = 1;
1360 node->data = NULL;
1361 node->datalen = 0;
1362 node->children = (char *)child;
1363 if (child)
1364 node->childlen = strlen(child) + 1;
1365 else
1366 node->childlen = 0;
1368 if (!write_node(NULL, node))
1369 barf_perror("Could not create initial node %s", name);
1370 talloc_free(node);
1375 static void setup_structure(void)
1377 char *tdbname;
1378 tdbname = talloc_strdup(talloc_autofree_context(), xs_daemon_tdb());
1379 tdb_ctx = tdb_open(tdbname, 0, TDB_FLAGS, O_RDWR, 0);
1381 if (!tdb_ctx) {
1382 tdb_ctx = tdb_open(tdbname, 7919, TDB_FLAGS, O_RDWR|O_CREAT,
1383 0640);
1384 if (!tdb_ctx)
1385 barf_perror("Could not create tdb file %s", tdbname);
1387 manual_node("/", "tool");
1388 manual_node("/tool", "xenstored");
1389 manual_node("/tool/xenstored", NULL);
1392 /* FIXME: Fsck */
1395 static void write_pidfile(const char *pidfile)
1397 char buf[100];
1398 int len;
1399 int fd;
1401 fd = open(pidfile, O_RDWR | O_CREAT, 0600);
1402 if (fd == -1)
1403 barf_perror("Opening pid file %s", pidfile);
1405 /* We exit silently if daemon already running. */
1406 if (lockf(fd, F_TLOCK, 0) == -1)
1407 exit(0);
1409 len = sprintf(buf, "%d\n", getpid());
1410 write(fd, buf, len);
1413 /* Stevens. */
1414 static void daemonize(void)
1416 pid_t pid;
1418 /* Separate from our parent via fork, so init inherits us. */
1419 if ((pid = fork()) < 0)
1420 barf_perror("Failed to fork daemon");
1421 if (pid != 0)
1422 exit(0);
1424 /* Session leader so ^C doesn't whack us. */
1425 setsid();
1426 #ifndef TESTING /* Relative paths for socket names */
1427 /* Move off any mount points we might be in. */
1428 chdir("/");
1429 #endif
1430 /* Discard our parent's old-fashioned umask prejudices. */
1431 umask(0);
1435 static void usage(void)
1437 fprintf(stderr,
1438 "Usage:\n"
1439 "\n"
1440 " xenstored <options>\n"
1441 "\n"
1442 "where options may include:\n"
1443 "\n"
1444 " --no-domain-init to state that xenstored should not initialise dom0,\n"
1445 " --pid-file <file> giving a file for the daemon's pid to be written,\n"
1446 " --help to output this message,\n"
1447 " --no-fork to request that the daemon does not fork,\n"
1448 " --output-pid to request that the pid of the daemon is output,\n"
1449 " --trace-file <file> giving the file for logging, and\n"
1450 " --verbose to request verbose execution.\n");
1454 static struct option options[] = {
1455 { "no-domain-init", 0, NULL, 'D' },
1456 { "pid-file", 1, NULL, 'F' },
1457 { "help", 0, NULL, 'H' },
1458 { "no-fork", 0, NULL, 'N' },
1459 { "output-pid", 0, NULL, 'P' },
1460 { "trace-file", 1, NULL, 'T' },
1461 { "verbose", 0, NULL, 'V' },
1462 { NULL, 0, NULL, 0 } };
1464 extern void dump_conn(struct connection *conn);
1466 int main(int argc, char *argv[])
1468 int opt, *sock, *ro_sock, max;
1469 struct sockaddr_un addr;
1470 fd_set inset, outset;
1471 bool dofork = true;
1472 bool outputpid = false;
1473 bool no_domain_init = false;
1474 const char *pidfile = NULL;
1476 while ((opt = getopt_long(argc, argv, "DF:HNPT:V", options,
1477 NULL)) != -1) {
1478 switch (opt) {
1479 case 'D':
1480 no_domain_init = true;
1481 break;
1482 case 'F':
1483 pidfile = optarg;
1484 break;
1485 case 'H':
1486 usage();
1487 return 0;
1488 case 'N':
1489 dofork = false;
1490 break;
1491 case 'P':
1492 outputpid = true;
1493 break;
1494 case 'T':
1495 tracefd = open(optarg, O_WRONLY|O_CREAT|O_APPEND, 0600);
1496 if (tracefd < 0)
1497 barf_perror("Could not open tracefile %s",
1498 optarg);
1499 write(tracefd, "\n***\n", strlen("\n***\n"));
1500 break;
1501 case 'V':
1502 verbose = true;
1503 break;
1506 if (optind != argc)
1507 barf("%s: No arguments desired", argv[0]);
1509 if (dofork) {
1510 openlog("xenstored", 0, LOG_DAEMON);
1511 daemonize();
1513 if (pidfile)
1514 write_pidfile(pidfile);
1516 talloc_enable_leak_report_full();
1518 /* Create sockets for them to listen to. */
1519 sock = talloc(talloc_autofree_context(), int);
1520 *sock = socket(PF_UNIX, SOCK_STREAM, 0);
1521 if (*sock < 0)
1522 barf_perror("Could not create socket");
1523 ro_sock = talloc(talloc_autofree_context(), int);
1524 *ro_sock = socket(PF_UNIX, SOCK_STREAM, 0);
1525 if (*ro_sock < 0)
1526 barf_perror("Could not create socket");
1527 talloc_set_destructor(sock, destroy_fd);
1528 talloc_set_destructor(ro_sock, destroy_fd);
1530 /* Don't kill us with SIGPIPE. */
1531 signal(SIGPIPE, SIG_IGN);
1533 /* FIXME: Be more sophisticated, don't mug running daemon. */
1534 unlink(xs_daemon_socket());
1535 unlink(xs_daemon_socket_ro());
1537 addr.sun_family = AF_UNIX;
1538 strcpy(addr.sun_path, xs_daemon_socket());
1539 if (bind(*sock, (struct sockaddr *)&addr, sizeof(addr)) != 0)
1540 barf_perror("Could not bind socket to %s", xs_daemon_socket());
1541 strcpy(addr.sun_path, xs_daemon_socket_ro());
1542 if (bind(*ro_sock, (struct sockaddr *)&addr, sizeof(addr)) != 0)
1543 barf_perror("Could not bind socket to %s",
1544 xs_daemon_socket_ro());
1545 if (chmod(xs_daemon_socket(), 0600) != 0
1546 || chmod(xs_daemon_socket_ro(), 0660) != 0)
1547 barf_perror("Could not chmod sockets");
1549 if (listen(*sock, 1) != 0
1550 || listen(*ro_sock, 1) != 0)
1551 barf_perror("Could not listen on sockets");
1553 /* Setup the database */
1554 setup_structure();
1556 /* Listen to hypervisor. */
1557 if (!no_domain_init)
1558 domain_init();
1560 /* Restore existing connections. */
1561 restore_existing_connections();
1563 if (outputpid) {
1564 printf("%i\n", getpid());
1565 fflush(stdout);
1568 /* close stdin/stdout now we're ready to accept connections */
1569 if (dofork) {
1570 close(STDIN_FILENO);
1571 close(STDOUT_FILENO);
1572 close(STDERR_FILENO);
1575 #ifdef TESTING
1576 signal(SIGUSR1, stop_failtest);
1577 #endif
1579 /* Get ready to listen to the tools. */
1580 max = initialize_set(&inset, &outset, *sock, *ro_sock);
1582 /* Main loop. */
1583 /* FIXME: Rewrite so noone can starve. */
1584 for (;;) {
1585 struct connection *i;
1587 if (select(max+1, &inset, &outset, NULL, NULL) < 0) {
1588 if (errno == EINTR)
1589 continue;
1590 barf_perror("Select failed");
1593 if (FD_ISSET(*sock, &inset))
1594 accept_connection(*sock, true);
1596 if (FD_ISSET(*ro_sock, &inset))
1597 accept_connection(*ro_sock, false);
1599 if (FD_ISSET(eventchn_fd, &inset))
1600 handle_event();
1602 list_for_each_entry(i, &connections, list) {
1603 if (i->domain)
1604 continue;
1606 /* Operations can delete themselves or others
1607 * (xs_release): list is not safe after input,
1608 * so break. */
1609 if (FD_ISSET(i->fd, &inset)) {
1610 handle_input(i);
1611 break;
1613 if (FD_ISSET(i->fd, &outset)) {
1614 handle_output(i);
1615 break;
1619 /* Handle all possible I/O for domain connections. */
1620 more:
1621 list_for_each_entry(i, &connections, list) {
1622 if (!i->domain)
1623 continue;
1625 if (domain_can_read(i)) {
1626 handle_input(i);
1627 goto more;
1630 if (domain_can_write(i) && !list_empty(&i->out_list)) {
1631 handle_output(i);
1632 goto more;
1636 max = initialize_set(&inset, &outset, *sock, *ro_sock);
1640 /*
1641 * Local variables:
1642 * c-file-style: "linux"
1643 * indent-tabs-mode: t
1644 * c-indent-level: 8
1645 * c-basic-offset: 8
1646 * tab-width: 8
1647 * End:
1648 */