1 /*
2  * driver.c: Helpers for loading drivers
3  *
4  * Copyright (C) 2006-2011 Red Hat, Inc.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library.  If not, see
18  * <http://www.gnu.org/licenses/>.
19  *
20  */
21 
22 
23 #include <config.h>
24 
25 #include <unistd.h>
26 
27 #include "driver.h"
28 #include "viralloc.h"
29 #include "virfile.h"
30 #include "virlog.h"
31 #include "virmodule.h"
32 #include "virobject.h"
33 #include "virstring.h"
34 #include "virthread.h"
35 #include "virutil.h"
36 #include "viridentity.h"
37 #include "datatypes.h"
38 #include "configmake.h"
39 
40 VIR_LOG_INIT("driver");
41 
42 #define VIR_FROM_THIS VIR_FROM_NONE
43 
44 /* XXX re-implement this for other OS, or use libtools helper lib ? */
45 #define DEFAULT_DRIVER_DIR LIBDIR "/libvirt/connection-driver"
46 
47 
48 
49 int
virDriverLoadModule(const char * name,const char * regfunc,bool required)50 virDriverLoadModule(const char *name,
51                     const char *regfunc,
52                     bool required)
53 {
54     g_autofree char *modfile = NULL;
55 
56     VIR_DEBUG("Module load %s", name);
57 
58     if (!(modfile = virFileFindResourceFull(name,
59                                             "libvirt_driver_",
60                                             VIR_FILE_MODULE_EXT,
61                                             abs_top_builddir "/src",
62                                             DEFAULT_DRIVER_DIR,
63                                             "LIBVIRT_DRIVER_DIR")))
64         return -1;
65 
66     return virModuleLoad(modfile, regfunc, required);
67 }
68 
69 
70 /* XXX unload modules, but we can't until we can unregister libvirt drivers */
71 
72 /**
73  * virDriverShouldAutostart:
74  * @dir: driver's run state directory (usually /var/run/libvirt/$driver)
75  * @autostart: whether driver should initiate autostart
76  *
77  * Automatic starting of libvirt's objects (e.g. domains, networks, storage
78  * pools, etc.) doesn't play nice with using '--timeout' on daemon's command
79  * line because the objects are attempted to autostart on every start of
80  * corresponding driver/daemon. To resolve this problem, a file is created in
81  * driver's private directory (which doesn't survive host's reboot) and thus
82  * autostart is attempted only once.
83  */
84 int
virDriverShouldAutostart(const char * dir,bool * autostart)85 virDriverShouldAutostart(const char *dir,
86                          bool *autostart)
87 {
88     g_autofree char *path = NULL;
89 
90     *autostart = false;
91 
92     path = g_strdup_printf("%s/autostarted", dir);
93 
94     if (virFileExists(path)) {
95         VIR_DEBUG("Autostart file %s exists, skipping autostart", path);
96         return 0;
97     }
98 
99     VIR_DEBUG("Autostart file %s does not exist, do autostart", path);
100     *autostart = true;
101 
102     if (virFileTouch(path, 0600) < 0)
103         return -1;
104 
105     return 0;
106 }
107 
108 
109 virThreadLocal connectInterface;
110 virThreadLocal connectNetwork;
111 virThreadLocal connectNWFilter;
112 virThreadLocal connectNodeDev;
113 virThreadLocal connectSecret;
114 virThreadLocal connectStorage;
115 
116 static int
virConnectCacheOnceInit(void)117 virConnectCacheOnceInit(void)
118 {
119     if (virThreadLocalInit(&connectInterface, NULL) < 0 ||
120         virThreadLocalInit(&connectNetwork, NULL) < 0 ||
121         virThreadLocalInit(&connectNWFilter, NULL) < 0 ||
122         virThreadLocalInit(&connectNodeDev, NULL) < 0 ||
123         virThreadLocalInit(&connectSecret, NULL) < 0 ||
124         virThreadLocalInit(&connectStorage, NULL) < 0) {
125         virReportSystemError(errno, "%s",
126                              _("Unable to initialize thread local variable"));
127         return -1;
128     }
129 
130     return 0;
131 }
132 
133 VIR_ONCE_GLOBAL_INIT(virConnectCache);
134 
135 static virConnectPtr
virGetConnectGeneric(virThreadLocal * threadPtr,const char * name)136 virGetConnectGeneric(virThreadLocal *threadPtr, const char *name)
137 {
138     virConnectPtr conn;
139     virErrorPtr orig_err;
140 
141     if (virConnectCacheInitialize() < 0)
142         return NULL;
143 
144     conn = virThreadLocalGet(threadPtr);
145 
146     if (conn) {
147         VIR_DEBUG("Return cached %s connection %p", name, conn);
148         virObjectRef(conn);
149     } else {
150         g_autofree char *uri = NULL;
151         const char *uriPath = geteuid() == 0 ? "/system" : "/session";
152 
153         uri = g_strdup_printf("%s://%s", name, uriPath);
154 
155         conn = virConnectOpen(uri);
156         VIR_DEBUG("Opened new %s connection %p", name, conn);
157         if (!conn)
158             return NULL;
159 
160         if (conn->driver->connectSetIdentity != NULL) {
161             g_autoptr(virIdentity) ident = NULL;
162             virTypedParameterPtr identparams = NULL;
163             int nidentparams = 0;
164 
165             VIR_DEBUG("Attempting to delegate current identity");
166             if (!(ident = virIdentityGetCurrent()))
167                 goto error;
168 
169             if (virIdentityGetParameters(ident, &identparams, &nidentparams) < 0)
170                 goto error;
171 
172             if (virConnectSetIdentity(conn, identparams, nidentparams, 0) < 0)
173                 goto error;
174         }
175     }
176     return conn;
177 
178  error:
179     virErrorPreserveLast(&orig_err);
180     virConnectClose(conn);
181     virErrorRestore(&orig_err);
182     return NULL;
183 }
184 
185 
virGetConnectInterface(void)186 virConnectPtr virGetConnectInterface(void)
187 {
188     return virGetConnectGeneric(&connectInterface, "interface");
189 }
190 
virGetConnectNetwork(void)191 virConnectPtr virGetConnectNetwork(void)
192 {
193     return virGetConnectGeneric(&connectNetwork, "network");
194 }
195 
virGetConnectNWFilter(void)196 virConnectPtr virGetConnectNWFilter(void)
197 {
198     return virGetConnectGeneric(&connectNWFilter, "nwfilter");
199 }
200 
virGetConnectNodeDev(void)201 virConnectPtr virGetConnectNodeDev(void)
202 {
203     return virGetConnectGeneric(&connectNodeDev, "nodedev");
204 }
205 
virGetConnectSecret(void)206 virConnectPtr virGetConnectSecret(void)
207 {
208     return virGetConnectGeneric(&connectSecret, "secret");
209 }
210 
virGetConnectStorage(void)211 virConnectPtr virGetConnectStorage(void)
212 {
213     return virGetConnectGeneric(&connectStorage, "storage");
214 }
215 
216 
217 int
virSetConnectInterface(virConnectPtr conn)218 virSetConnectInterface(virConnectPtr conn)
219 {
220     if (virConnectCacheInitialize() < 0)
221         return -1;
222 
223     VIR_DEBUG("Override interface connection with %p", conn);
224     return virThreadLocalSet(&connectInterface, conn);
225 }
226 
227 
228 int
virSetConnectNetwork(virConnectPtr conn)229 virSetConnectNetwork(virConnectPtr conn)
230 {
231     if (virConnectCacheInitialize() < 0)
232         return -1;
233 
234     VIR_DEBUG("Override network connection with %p", conn);
235     return virThreadLocalSet(&connectNetwork, conn);
236 }
237 
238 
239 int
virSetConnectNWFilter(virConnectPtr conn)240 virSetConnectNWFilter(virConnectPtr conn)
241 {
242     if (virConnectCacheInitialize() < 0)
243         return -1;
244 
245     VIR_DEBUG("Override nwfilter connection with %p", conn);
246     return virThreadLocalSet(&connectNWFilter, conn);
247 }
248 
249 
250 int
virSetConnectNodeDev(virConnectPtr conn)251 virSetConnectNodeDev(virConnectPtr conn)
252 {
253     if (virConnectCacheInitialize() < 0)
254         return -1;
255 
256     VIR_DEBUG("Override nodedev connection with %p", conn);
257     return virThreadLocalSet(&connectNodeDev, conn);
258 }
259 
260 
261 int
virSetConnectSecret(virConnectPtr conn)262 virSetConnectSecret(virConnectPtr conn)
263 {
264     if (virConnectCacheInitialize() < 0)
265         return -1;
266 
267     VIR_DEBUG("Override secret connection with %p", conn);
268     return virThreadLocalSet(&connectSecret, conn);
269 }
270 
271 
272 int
virSetConnectStorage(virConnectPtr conn)273 virSetConnectStorage(virConnectPtr conn)
274 {
275     if (virConnectCacheInitialize() < 0)
276         return -1;
277 
278     VIR_DEBUG("Override storage connection with %p", conn);
279     return virThreadLocalSet(&connectStorage, conn);
280 }
281 
282 bool
virConnectValidateURIPath(const char * uriPath,const char * entityName,bool privileged)283 virConnectValidateURIPath(const char *uriPath,
284                           const char *entityName,
285                           bool privileged)
286 {
287     if (privileged) {
288         /* TODO: qemu and vbox drivers allow '/session'
289          * connections as root. This is not ideal, but changing
290          * these drivers to refuse privileged '/session'
291          * connections, like everyone else is already doing, can
292          * break existing applications. Until we decide what to do,
293          * for now we can handle them as exception in this validate
294          * function.
295          */
296         bool compatSessionRoot = (STREQ(entityName, "qemu") ||
297                                   STREQ(entityName, "vbox")) &&
298                                   STREQ(uriPath, "/session");
299 
300         if (STRNEQ(uriPath, "/system") && !compatSessionRoot) {
301             virReportError(VIR_ERR_INTERNAL_ERROR,
302                            _("unexpected %s URI path '%s', try "
303                              "%s:///system"),
304                            entityName, uriPath, entityName);
305             return false;
306         }
307     } else {
308         if (STRNEQ(uriPath, "/session")) {
309             virReportError(VIR_ERR_INTERNAL_ERROR,
310                            _("unexpected %s URI path '%s', try "
311                              "%s:///session"),
312                            entityName, uriPath, entityName);
313             return false;
314         }
315     }
316 
317     return true;
318 }
319