2
* Copyright (c) 2011-2013, The Bumblebee Project
3
* Author: Joaquín Ignacio Aramendía <samsagax@gmail.com>
4
* Author: Jaron Viëtor AKA "Thulinma" <jaron@vietors.com>
6
* This file is part of Bumblebee.
8
* Bumblebee is free software: you can redistribute it and/or modify
9
* it under the terms of the GNU General Public License as published by
10
* the Free Software Foundation, either version 3 of the License, or
11
* (at your option) any later version.
13
* Bumblebee is distributed in the hope that it will be useful,
14
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
* GNU General Public License for more details.
18
* You should have received a copy of the GNU General Public License
19
* along with Bumblebee. If not, see <http://www.gnu.org/licenses/>.
23
* C-coded version of the Bumblebee daemon and optirun.
26
#include <sys/types.h>
38
#ifdef HAVE_LIBBSD_020
41
#include <bsd/libutil.h>
47
#include "bbsecondary.h"
51
#include "switch/switching.h"
54
* Change GID and umask of the daemon
55
* @return EXIT_SUCCESS if the gid could be changed, EXIT_FAILURE otherwise
57
static int bb_chgid(void) {
58
/* Change the Group ID of bumblebee */
61
gp = getgrnam(bb_config.gid_name);
63
int error_num = errno;
64
bb_log(LOG_ERR, "%s\n", strerror(error_num));
65
bb_log(LOG_ERR, "There is no \"%s\" group\n", bb_config.gid_name);
68
if (setgid(gp->gr_gid) != 0) {
69
bb_log(LOG_ERR, "Could not set the GID of bumblebee: %s\n", strerror(errno));
72
/* Change the file mode mask */
78
* Fork to the background, and exit parent.
79
* @return EXIT_SUCCESS if the daemon could fork, EXIT_FAILURE otherwise. Note
80
* that the parent exits and the child continues to run
82
static int daemonize(void) {
83
/* Fork off the parent process */
84
pid_t bb_pid = fork();
86
bb_log(LOG_ERR, "Could not fork to background\n");
90
/* If we got a good PID, then we can exit the parent process. */
95
/* Create a new SID for the child process */
96
pid_t bb_sid = setsid();
98
bb_log(LOG_ERR, "Could not set SID: %s\n", strerror(errno));
102
/* Change the current working directory */
103
if ((chdir("/")) < 0) {
104
bb_log(LOG_ERR, "Could not change to root directory: %s\n", strerror(errno));
108
/* Reroute standard file descriptors to /dev/null */
109
int devnull = open("/dev/null", O_RDWR);
111
bb_log(LOG_ERR, "Could not open /dev/null: %s\n", strerror(errno));
114
dup2(devnull, STDIN_FILENO);
115
dup2(devnull, STDOUT_FILENO);
116
dup2(devnull, STDERR_FILENO);
122
* Handle recieved signals - except SIGCHLD, which is handled in bbrun.c
124
static void handle_signal(int sig) {
125
static int sigpipes = 0;
129
bb_log(LOG_WARNING, "Received %s signal (ignoring...)\n", strsignal(sig));
132
/* if bb_log generates a SIGPIPE, i.e. when bumblebeed runs like
133
* bumblebeed 2>&1 | cat and the pipe is killed, don't die infinitely */
134
if (sigpipes <= 10) {
135
bb_log(LOG_WARNING, "Received %s signal %i (signals 10> are ignored)\n",
136
strsignal(sig), ++sigpipes);
141
bb_log(LOG_WARNING, "Received %s signal.\n", strsignal(sig));
142
socketClose(&bb_status.bb_socket); //closing the socket terminates the server
145
bb_log(LOG_WARNING, "Received %s signal.\n", strsignal(sig));
146
socketClose(&bb_status.bb_socket); //closing the socket terminates the server
147
bb_run_stopwaiting(); //speed up shutdown by not waiting for processes anymore
150
bb_log(LOG_WARNING, "Unhandled signal %s\n", strsignal(sig));
155
/// Socket list structure for use in main_loop.
157
struct clientsocket {
160
struct clientsocket * prev;
161
struct clientsocket * next;
164
/// Receive and/or sent data to/from this socket.
165
/// \param sock Pointer to socket. Assumed to be valid.
167
static void handle_socket(struct clientsocket * C) {
168
static char buffer[BUFFER_SIZE], *conf_key;
170
//since these are local sockets, we can safely assume we get whole messages at a time
171
int r = socketRead(&C->sock, buffer, BUFFER_SIZE);
173
ensureZeroTerminated(buffer, r, BUFFER_SIZE);
174
conf_key = strchr(buffer, ' ');
177
if (bb_status.errors[0] != 0) {
178
r = snprintf(buffer, BUFFER_SIZE, "Error (%s): %s\n", GITVERSION, bb_status.errors);
180
if (bb_is_running(bb_status.x_pid)) {
181
r = snprintf(buffer, BUFFER_SIZE, "Ready (%s). X is PID %i, %i applications using bumblebeed.\n", GITVERSION, bb_status.x_pid, bb_status.appcount);
184
switch (switch_status()) {
192
/* no PM available, assume it's on */
193
card_status = "likely on";
196
r = snprintf(buffer, BUFFER_SIZE, "Ready (%s). X inactive. Discrete"
197
" video card is %s.\n", GITVERSION, card_status);
200
/* don't rely on result of snprintf, instead calculate length including
201
* null byte. We assume a succesful write */
202
socketWrite(&C->sock, buffer, strlen(buffer) + 1);
204
case 'F'://force VirtualGL if possible
205
case 'C'://check if VirtualGL is allowed
206
need_secondary = conf_key ? strcmp(conf_key + 1, "NoX") : true;
207
if (start_secondary(need_secondary)) {
208
r = snprintf(buffer, BUFFER_SIZE, "Yes. X is active.\n");
211
bb_status.appcount++;
214
if (bb_status.errors[0] != 0) {
215
r = snprintf(buffer, BUFFER_SIZE, "No - error: %s\n", bb_status.errors);
217
r = snprintf(buffer, BUFFER_SIZE, "No, secondary X is not active.\n");
220
/* don't rely on result of snprintf, instead calculate length including
221
* null byte. We assume a succesful write */
222
socketWrite(&C->sock, buffer, strlen(buffer) + 1);
224
case 'D'://done, close the socket.
225
socketClose(&C->sock);
227
case 'Q': /* query for configuration details */
228
/* required since labels can only be attached on statements */;
231
if (strcmp(conf_key, "VirtualDisplay") == 0) {
232
snprintf(buffer, BUFFER_SIZE, "Value: %s\n", bb_config.x_display);
233
} else if (strcmp(conf_key, "LibraryPath") == 0) {
234
snprintf(buffer, BUFFER_SIZE, "Value: %s\n", bb_config.ld_path);
235
} else if (strcmp(conf_key, "Driver") == 0) {
236
/* note: this is not the auto-detected value, but the actual one */
237
snprintf(buffer, BUFFER_SIZE, "Value: %s\n", bb_config.driver);
239
snprintf(buffer, BUFFER_SIZE, "Unknown key requested.\n");
242
snprintf(buffer, BUFFER_SIZE, "Error: invalid protocol message.\n");
244
socketWrite(&C->sock, buffer, strlen(buffer) + 1);
247
bb_log(LOG_WARNING, "Unhandled message received: %s\n", buffer);
253
/* The main loop handles all connections and cleanup.
254
* It returns if there are any problems with the listening socket.
256
static void main_loop(void) {
257
int optirun_socket_fd;
258
struct clientsocket *client;
259
struct clientsocket *last = 0; // the last client
261
bb_log(LOG_INFO, "Initialization completed - now handling client requests\n");
262
/* Listen for Optirun conections and act accordingly */
263
while (bb_status.bb_socket != -1) {
268
#define FD_SET_AND_MAX(fd) \
269
do if ((fd) >= 0 && (fd) < FD_SETSIZE) { \
270
FD_SET((fd), &readfds); \
274
FD_SET_AND_MAX(bb_status.bb_socket);
275
FD_SET_AND_MAX(bb_status.x_pipe[0]);
276
for (client = last; client; client = client->prev)
277
FD_SET_AND_MAX(client->sock);
278
#undef FD_SET_AND_MAX
280
if (select(max_fd + 1, &readfds, NULL, NULL, NULL) < 0) {
283
bb_log(LOG_ERR, "select() failed: %s\n", strerror(errno));
287
#define FD_EVENT(fd) ((fd) >= 0 && FD_ISSET((fd), &readfds))
288
if (FD_EVENT(bb_status.bb_socket)) {
289
/* Accept a connection. */
290
optirun_socket_fd = socketAccept(&bb_status.bb_socket, SOCK_NOBLOCK);
291
if (optirun_socket_fd >= 0) {
292
bb_log(LOG_DEBUG, "Accepted new connection\n", optirun_socket_fd, bb_status.appcount);
294
/* add to list of sockets */
295
client = malloc(sizeof (struct clientsocket));
296
client->sock = optirun_socket_fd;
307
//check the X output pipe, if open
308
if (FD_EVENT(bb_status.x_pipe[0]))
311
/* loop through all connections, removing dead ones, receiving/sending data to the rest */
312
struct clientsocket *next_iter;
313
for (client = last; client; client = next_iter) {
314
/* set the next client here because client may be free()'d */
315
next_iter = client->prev;
316
if (FD_EVENT(client->sock))
317
handle_socket(client);
318
if (client->sock < 0) {
320
if (client->inuse > 0) {
321
bb_status.appcount--;
322
//stop X / card if there is no need to keep it running
323
if ((bb_status.appcount == 0) && (bb_config.stop_on_exit)) {
328
client->next->prev = client->prev;
333
client->prev->next = client->next;
339
}//socket server loop
341
/* loop through all connections, closing all of them */
344
//close socket if not already closed
345
if (client->sock >= 0) {
346
socketClose(&client->sock);
349
if (client->inuse > 0) {
350
bb_status.appcount--;
352
// change the client here because after free() there is no way to know prev
354
client = client->prev;
357
if (bb_status.appcount != 0) {
358
bb_log(LOG_WARNING, "appcount = %i (should be 0)\n", bb_status.appcount);
363
* Returns the option string for this program
364
* @return An option string which can be used for getopt
366
const char *bbconfig_get_optstr(void) {
367
return BBCONFIG_COMMON_OPTSTR "Dx:g:m:k:";
371
* Returns the long options for this program
372
* @return A option struct which can be used for getopt_long
374
const struct option *bbconfig_get_lopts(void) {
375
static struct option longOpts[] = {
376
{"daemon", 0, 0, 'D'},
377
{"xconf", 1, 0, 'x'},
378
{"xconfdir", 1, 0, OPT_X_CONF_DIR_PATH},
379
{"group", 1, 0, 'g'},
380
{"module-path", 1, 0, 'm'},
381
{"driver-module", 1, 0, 'k'},
382
{"driver", 1, 0, OPT_DRIVER},
384
{"pidfile", 1, 0, OPT_PIDFILE},
386
{"use-syslog", 0, 0, OPT_USE_SYSLOG},
387
{"pm-method", 1, 0, OPT_PM_METHOD},
388
BBCONFIG_COMMON_LOPTS
394
* Parses local command line options
395
* @param opt The short option
396
* @param value Value for the option if any
397
* @return 1 if the option has been processed, 0 otherwise
399
int bbconfig_parse_options(int opt, char *value) {
402
/* already processed in bbconfig.c */
405
bb_status.runmode = BB_RUN_DAEMON;
407
case 'x'://xorg.conf path
408
set_string_value(&bb_config.x_conf_file, value);
410
case OPT_X_CONF_DIR_PATH://xorg.conf.d path
411
set_string_value(&bb_config.x_conf_dir, value);
413
case 'g'://group name to use
414
set_string_value(&bb_config.gid_name, value);
416
case 'm'://modulepath
417
set_string_value(&bb_config.mod_path, value);
419
case OPT_DRIVER://driver
420
set_string_value(&bb_config.driver, value);
422
case 'k'://kernel module
423
set_string_value(&bb_config.module_name, value);
426
bb_config.pm_method = bb_pm_method_from_string(value);
430
set_string_value(&bb_config.pid_file, value);
434
/* no options parsed */
440
int main(int argc, char* argv[]) {
442
struct pidfh *pfh = NULL;
446
/* the logs needs to be ready before the signal handlers */
447
init_early_config(argv, BB_RUN_SERVER);
448
bbconfig_parse_opts(argc, argv, PARSE_STAGE_LOG);
451
/* Setup signal handling before anything else. Note that messages are not
452
* shown until init_config has set bb_status.verbosity
454
signal(SIGHUP, handle_signal);
455
signal(SIGTERM, handle_signal);
456
signal(SIGINT, handle_signal);
457
signal(SIGQUIT, handle_signal);
458
signal(SIGPIPE, handle_signal);
460
/* first load the config to make the logging verbosity level available */
462
bbconfig_parse_opts(argc, argv, PARSE_STAGE_PRECONF);
464
/* First look for an intel card */
465
struct pci_bus_id *pci_id_igd = pci_find_gfx_by_vendor(PCI_VENDOR_ID_INTEL, 0);
467
/* This is no Optimus configuration. But maybe it's a
468
dual-nvidia configuration. Let us test that.
470
pci_id_igd = pci_find_gfx_by_vendor(PCI_VENDOR_ID_NVIDIA, 1);
471
bb_log(LOG_INFO, "No Intel video card found, testing for dual-nvidia system.\n");
474
/* Ok, this is not a double gpu setup supported (there is at most
475
one nvidia and no intel cards */
476
bb_log(LOG_ERR, "No integrated video card found, quitting.\n");
477
return (EXIT_FAILURE);
480
pci_bus_id_discrete = pci_find_gfx_by_vendor(PCI_VENDOR_ID_NVIDIA, 0);
481
if (!pci_bus_id_discrete) {
482
bb_log(LOG_ERR, "No discrete video card found, quitting\n");
483
return (EXIT_FAILURE);
486
bb_log(LOG_DEBUG, "Found card: %02x:%02x.%x (discrete)\n", pci_bus_id_discrete->bus, pci_bus_id_discrete->slot, pci_bus_id_discrete->func);
487
bb_log(LOG_DEBUG, "Found card: %02x:%02x.%x (integrated)\n", pci_id_igd->bus, pci_id_igd->slot, pci_id_igd->func);
491
GKeyFile *bbcfg = bbconfig_parse_conf();
492
bbconfig_parse_opts(argc, argv, PARSE_STAGE_DRIVER);
495
bbconfig_parse_conf_driver(bbcfg, bb_config.driver);
496
g_key_file_free(bbcfg);
498
bbconfig_parse_opts(argc, argv, PARSE_STAGE_OTHER);
501
/* dump the config after detecting the driver */
503
if (config_validate() != 0) {
504
return (EXIT_FAILURE);
508
/* only write PID if a pid file has been set */
509
if (bb_config.pid_file[0]) {
510
pfh = pidfile_open(bb_config.pid_file, 0644, &otherpid);
512
if (errno == EEXIST) {
513
bb_log(LOG_ERR, "Daemon already running, pid %d\n", otherpid);
515
bb_log(LOG_ERR, "Cannot open or write pidfile %s.\n", bb_config.pid_file);
523
/* Change GID and mask according to configuration */
524
if ((bb_config.gid_name != 0) && (bb_config.gid_name[0] != 0)) {
525
int retval = bb_chgid();
526
if (retval != EXIT_SUCCESS) {
535
bb_log(LOG_NOTICE, "%s %s started\n", bb_status.program_name, GITVERSION);
537
/* Daemonized if daemon flag is activated */
538
if (bb_status.runmode == BB_RUN_DAEMON) {
539
int retval = daemonize();
540
if (retval != EXIT_SUCCESS) {
550
/* write PID after daemonizing */
554
/* Initialize communication socket, enter main loop */
555
bb_status.bb_socket = socketServer(bb_config.socket_path, SOCK_NOBLOCK);
556
stop_secondary(); //turn off card, nobody is connected right now.
558
unlink(bb_config.socket_path);
559
bb_status.runmode = BB_RUN_EXIT; //make sure all methods understand we are shutting down
560
if (bb_config.card_shutdown_state) {
561
//if shutdown state = 1, turn on card
562
start_secondary(false);
564
//if shutdown state = 0, turn off card
571
bb_stop_all(); //stop any started processes that are left
572
//close X pipe, if any parts of it are open still
573
if (bb_status.x_pipe[0] != -1){close(bb_status.x_pipe[0]); bb_status.x_pipe[0] = -1;}
574
if (bb_status.x_pipe[1] != -1){close(bb_status.x_pipe[1]); bb_status.x_pipe[1] = -1;}
575
return (EXIT_SUCCESS);