Merge "master" into "wdp".
[sliver-openvswitch.git] / xenserver / ovs-xenserverd.c
1 /* Copyright (c) 2010 Nicira Networks
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <config.h>
17
18 #include <errno.h>
19 #include <getopt.h>
20 #include <signal.h>
21 #include <stdlib.h>
22 #include <sys/stat.h>
23
24 #include "command-line.h"
25 #include "daemon.h"
26 #include "dirs.h"
27 #include "poll-loop.h"
28 #include "process.h"
29 #include "socket-util.h"
30 #include "timeval.h"
31 #include "unixctl.h"
32 #include "util.h"
33
34 #define THIS_MODULE VLM_xenserverd
35 #include "vlog.h"
36
37 static void parse_options(int argc, char *argv[]);
38 static void usage(void) NO_RETURN;
39
40 static void network_uuid_refresh_run(void);
41 static void network_uuid_refresh_wait(void);
42
43 int
44 main(int argc, char *argv[])
45 {
46     struct unixctl_server *unixctl;
47     int retval;
48
49     proctitle_init(argc, argv);
50     set_program_name(argv[0]);
51     time_init();
52     vlog_init();
53     parse_options(argc, argv);
54     signal(SIGPIPE, SIG_IGN);
55     process_init();
56
57     die_if_already_running();
58     daemonize_start();
59
60     retval = unixctl_server_create(NULL, &unixctl);
61     if (retval) {
62         exit(EXIT_FAILURE);
63     }
64
65     daemonize_complete();
66
67     for (;;) {
68         network_uuid_refresh_run();
69         unixctl_server_run(unixctl);
70
71         network_uuid_refresh_wait();
72         unixctl_server_wait(unixctl);
73
74         poll_block();
75     }
76
77     return 0;
78 }
79
80
81 static void
82 parse_options(int argc, char *argv[])
83 {
84     enum {
85         VLOG_OPTION_ENUMS
86     };
87     static struct option long_options[] = {
88         {"help",        no_argument, 0, 'h'},
89         {"version",     no_argument, 0, 'V'},
90         DAEMON_LONG_OPTIONS,
91         VLOG_LONG_OPTIONS,
92         {0, 0, 0, 0},
93     };
94     char *short_options = long_options_to_short_options(long_options);
95
96     for (;;) {
97         int c;
98
99         c = getopt_long(argc, argv, short_options, long_options, NULL);
100         if (c == -1) {
101             break;
102         }
103
104         switch (c) {
105         case 'H':
106         case 'h':
107             usage();
108
109         case 'V':
110             OVS_PRINT_VERSION(0, 0);
111             exit(EXIT_SUCCESS);
112
113         VLOG_OPTION_HANDLERS
114         DAEMON_OPTION_HANDLERS
115
116         case '?':
117             exit(EXIT_FAILURE);
118
119         default:
120             abort();
121         }
122     }
123     free(short_options);
124
125     if (optind != argc) {
126         ovs_fatal(0, "no non-option arguments accepted");
127     }
128 }
129
130 static void
131 usage(void)
132 {
133     printf("%s: Open vSwitch daemon for XenServer-specific functionality\n"
134            "usage: %s [OPTIONS]\n", program_name, program_name);
135     daemon_usage();
136     vlog_usage();
137     printf("\nOther options:\n"
138            "  -h, --help              display this help message\n"
139            "  -V, --version           display version information\n");
140     exit(EXIT_SUCCESS);
141 }
142 \f
143 /* Network UUID refreshing.
144  *
145  * The vswitch database is supposed to maintain an up-to-date UUID for the
146  * system's networks in the Bridge table as external-ids:network-uuids.  On
147  * XenServer systems, /opt/xensource/libexec/interface-reconfigure updates
148  * these fields as bridges are brought up and down.  Most of the time, that is
149  * sufficient.  However, this is one exception: when a XenServer host enters or
150  * leaves a pool, interface-reconfigure is not invoked, and neither is any
151  * other script.  So we need to monitor the XenServer's pool membership status
152  * and refresh the network UUIDs (by invoking the refresh-network-uuids script)
153  * if it changes.
154  *
155  * This functionality should be harmless on non-XenServer systems, since they
156  * will have neither /etc/xensource/pool.conf nor refresh-network-uuids.
157  */
158
159 /* Timestamp of /etc/xensource/pool.conf, or zeros if it does not exist. */
160 static struct timespec pool_conf_mtime;
161
162 /* The executing instance of refresh-network-uuids, or NULL if none. */
163 static struct process *refresh_script;
164
165 /* Time at which to start the refresh script. */
166 static long long int next_refresh = LLONG_MAX;
167
168 static void
169 network_uuid_refresh_run(void)
170 {
171     struct timespec new_mtime;
172
173     /* If a script is running, don't do anything until it finishes. */
174     if (refresh_script) {
175         char *s;
176
177         if (!process_exited(refresh_script)) {
178             return;
179         }
180
181         s = process_status_msg(process_status(refresh_script));
182         VLOG_INFO("refresh-network-uuids exited, %s", s);
183         free(s);
184
185         process_destroy(refresh_script);
186         refresh_script = NULL;
187     }
188
189     /* Otherwise, schedule a refresh in a few seconds if the timestamp has
190      * changed.  Refreshing immediately doesn't work because XAPI takes a while
191      * to switch over to new UUIDs.
192      *
193      * (We will always detect a change in timestamp when we start up.  That's
194      * good, since it means that the refresh-network-uuids script gets
195      * thoroughly tested and we can't miss pool changes that happen when
196      * ovs-vswitchd isn't running.)  */
197     get_mtime("/etc/xensource/pool.conf", &new_mtime);
198     if (new_mtime.tv_sec != pool_conf_mtime.tv_sec
199         || new_mtime.tv_nsec != pool_conf_mtime.tv_nsec) {
200         next_refresh = time_msec() + 10 * 1000;
201         return;
202     }
203
204     /* Otherwise, if our timer expired then start the refresh. */
205     if (time_msec() >= next_refresh) {
206         struct stat s;
207         char *argv[2];
208
209         next_refresh = LLONG_MAX;
210
211         argv[0] = xasprintf("%s/scripts/refresh-network-uuids",
212                             ovs_pkgdatadir);
213         argv[1] = NULL;
214
215         if (!stat(argv[0], &s)) {
216             int error = process_start(argv, NULL, 0, NULL, 0, &refresh_script);
217             if (error) {
218                 VLOG_ERR("failed to refresh network UUIDs: %s could "
219                          "not be started (%s)", argv[0], strerror(error));
220             } else {
221                 VLOG_INFO("refreshing network UUIDs: started %s", argv[0]);
222             }
223         } else {
224             VLOG_ERR("failed to refresh network UUIDs: could not stat %s (%s)",
225                      argv[0], strerror(errno));
226         }
227
228         pool_conf_mtime = new_mtime;
229         free(argv[0]);
230     }
231 }
232
233 void
234 network_uuid_refresh_wait(void)
235 {
236     if (refresh_script) {
237         process_wait(refresh_script);
238     } else {
239         if (pool_conf_mtime.tv_sec) {
240             poll_timer_wait(1000);
241         }
242         if (next_refresh != LLONG_MAX) {
243             poll_timer_wait(next_refresh - time_msec());
244         }
245     }
246 }