/*
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files(the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice(including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <dirent.h>
#include <synch.h>
#include <signal.h>
#include <libgen.h>
#include "pciaccess.h"
#define IS_VGA(c) \
(((c) & 0x00ffff00) \
typedef struct {
int display;
typedef struct {
int display;
} ck_session_t;
typedef struct {
} shared_mem_t;
static struct sol_device_private {
const char *device_string;
};
static char *program_name;
static int disp_num = 0;
static int num_sessions = 0;
static int shmid;
static Bool
{
char *token;
int i;
if (debug)
/* parse cmd */
i = -1;
/* walk through other tokens */
if (++i == MAX_EARGC) {
return FALSE;
}
}
if (i == -1) {
return FALSE;
}
perror("unable to pipe");
return FALSE;
}
perror("can not fork");
return FALSE;
} else if (pid == 0) {
/* child process */
perror("child failed to reopen stdout");
return FALSE;
}
}
_exit(1);
} else {
/* parent process */
char *buf;
int status;
int len;
/* only need one line from read */
if (!done) {
}
}
else
}
} else {
}
perror("error waiting for child process");
return FALSE;
} else if (WIFSIGNALED(status)) {
return FALSE;
return TRUE;
} else {
return FALSE;
}
}
}
static void
get_dsession_mgr_table(void)
{
int i;
for (i = 0; i < MAX_DISP_DEV; i++) {
if (!dsession_mgr_table[i].dev_path[0])
break;
}
if (i == 0)
printf("warning: no entry in dsession table\n");
disp_num = i;
}
static void
{
else
else
else
printf("\tdisplay = :\n");
else
else
printf("\tuser_session_id = %s\n",
}
static Bool
{
int i;
if (all) {
for (i = 0; i < disp_num; i++)
} else {
for (i = 0; i < disp_num; i++) {
break;
}
if (i != disp_num)
else {
return FALSE;
}
}
return TRUE;
}
static int
{
perror("get_sessions: unable to pipe");
return -1;
}
perror("get_sessions: can not fork");
return -1;
} else if (pid == 0) {
/* child process */
perror("get_sessions: child failed to reopen stdout");
return -1;
}
}
NULL);
else
_exit(1);
} else {
/* parent process */
int session_num;
int status;
session_num = -1;
if (discard)
continue;
if (buf[0] != '\t') {
/* session id */
continue;
}
bufsize--;
}
} else {
/* session body */
sizeof ("'TRUE'") - 1) == 0)
sizeof ("'FALSE'") - 1) == 0)
sizeof ("\tx11-display = ") - 1) == 0) {
int disp;
sizeof ("''") - 1) == 0)
else {
&disp) == 1)
}
sizeof ("\tsession-type = ") - 1) == 0) {
sizeof ("''") - 1) == 0)
else {
type) == 1)
}
sizeof ("\tdisplay-type = ") - 1) == 0) {
sizeof ("''") - 1) == 0)
else {
type) == 1)
}
} else
/* ignore */
continue;
}
}
perror("get_sessions: error waiting for child process");
return -1;
} else if (WIFSIGNALED(status)) {
return -1;
if (ret) {
if (debug) {
printf("get_sessions(all): %d sessions found\n",
session_num + 1);
else
printf("get_sessions(single): %d sessions found\n",
session_num + 1);
}
return session_num + 1;
} else
return -1;
} else {
return -1;
}
}
}
static int
{
int i;
if (!session_id)
return -1;
for (i = 0; i < num_sessions; i++) {
return i;
}
return -1;
}
static Bool
probe_and_print_dev(void)
{
if (pci_system_init() != 0) {
return FALSE;
}
}
}
return TRUE;
}
static Bool
set_server_pid(int i)
{
long pid;
else
return FALSE;
if (debug)
printf("server pid %ld set for display :%d\n",
(long) dsession_mgr_table[i].server_pid,
return TRUE;
} else
return FALSE;
}
return FALSE;
}
static Bool
{
int count = 0;
int i, j;
char *session_id;
/* called from init() or fini() */
allsessions = TRUE;
else {
allsessions = FALSE;
}
usleep(2000000);
if (allsessions) {
/* delete all sessions, only when called from fini() */
return FALSE;
for (i = 0; i < disp_num; i++) {
!= 0) {
if ((find_session_in_list(
/*
* session closed, also need to verify server
* termination before exiting
*/
if (server_pid != -1) {
if (kill(server_pid, 0)) {
if (debug)
printf("server %ld terminated\n",
(long) server_pid);
} else
} else
if (verified) {
"Deleted", SESSION_SIZE);
"REMOVED", TYPE_SIZE);
}
} else {
(server_pid != -1)) {
errno = 0;
perror("kill server failed");
else if (debug)
printf("server %ld killed\n",
(long) server_pid);
}
}
}
}
if (verified && no_server_pid)
/*
* add a delay if server's termination cannot be
* verified
*/
usleep(4000000);
} else {
/* delete single session */
if (get_sessions(session_id) == 0) {
/*
* session closed, also need to verify server
* termination before exiting
*/
if (server_pid != -1) {
if (kill(server_pid, 0)) {
if (debug)
printf("server %ld terminated\n",
(long) server_pid);
}
} else {
/*
* add a delay if server's termination cannot be
* verified
*/
usleep(4000000);
}
if (verified) {
"Deleted", SESSION_SIZE);
"REMOVED", TYPE_SIZE);
sizeof (dsession_mgr_t));
}
} else {
(server_pid != -1)) {
errno = 0;
perror("kill server failed");
else if (debug)
printf("server %ld killed\n",
(long) server_pid);
}
}
}
}
do {
usleep(2000000);
if (allsessions) {
/* add all sessions, only when called from init() */
return FALSE;
for (i = 0; i < disp_num; i++) {
if ((j = find_session_in_list(
if ((!ck_sessions[j].open) ||
} else
}
} else {
/* add single session */
i = get_sessions(session_id);
}
if (allsessions) {
/* add all sessions */
for (i = 0; i < disp_num; i++) {
if ((j = find_session_in_list(
/* consolekit session was created */
if (dsession_mgr_table[i].open) {
"UP", TYPE_SIZE);
if (!set_server_pid(i)) {
}
} else {
"FAILURE", TYPE_SIZE);
}
} else
}
} else {
/* Add single session */
== 0)) {
/* consolekit session was created */
"UP", TYPE_SIZE);
if (!set_server_pid(index)) {
}
} else
"FAILURE", TYPE_SIZE);
sizeof (dsession_mgr_t));
}
}
}
return verified;
}
static int
delete_session(void)
{
int i;
for (i = 0; i < disp_num; i++) {
break;
}
}
if (i == disp_num) {
return -1;
}
if ((!dsession_mgr_table[i].session_id[0]) ||
/*
* no valid session id in mgr table, this is abnormal.
* try to work around.
*/
int j;
return -1;
for (j = 0; j < num_sessions; j++) {
break;
}
}
}
/* session already deleted */
printf("dsession for device %s already deleted\n",
return i;
}
}
if (!dsession_mgr_table[i].open &&
dsession_mgr_table[i].usr_session[0]) {
/* has a user session for it, delete it first */
printf("warning: you are deleting a user session\n");
"/usr/sbin/ck-seat-tool --delete --session-id=%s",
dsession_mgr_table[i].usr_session[0] = 0;
printf("user session %s deleted\n",
}
}
"/usr/sbin/ck-seat-tool --delete --session-id=%s",
return i;
} else {
return -1;
}
}
static int
{
int index;
int display;
if (init) {
/* called from init() */
/* avoid using display :0 */
} else {
break;
}
/* add session on existing device */
!= 0)) {
printf("session for device already running - not added\n");
return -1;
}
/*
* check to see if a ck session already running on
* the same display. this could happen when previous
* verify_and_set() got interrupted .
*/
int i;
return -1;
for (i = 0; i < num_sessions; i++) {
if (ck_sessions[i].display ==
printf("%s for device %s restored in table\n",
break;
}
}
if (i < num_sessions)
return index;
}
} else if (disp_num == MAX_DISP_DEV) {
/* index == disp_num, and adding a new session */
return -1;
}
/*
* unless called from init(), always need a re-probe, as the numbering
* of root complex may have changed. So table's busid needs update if
* the device already exits in the table.
*/
if (pci_system_init() != 0) {
return -1;
}
else
continue;
== -1)
} else {
/* add a session for new device */
PATH_MAX);
disp_num++;
}
break;
}
}
}
if (!found) {
return -1;
}
}
/* prepare command */
"/usr/sbin/ck-seat-tool -a --session-type=LoginWindow"
" --display-type=HotPlug --seat-id=StaticSeat1"
" display=:%d busid=%s",
/* run command */
return index;
} else {
return -1;
}
}
static Bool
restart_session(void)
{
int i;
if ((i = delete_session()) == -1) {
return FALSE;
} else {
if (!verify_and_set("delete", i)) {
"restart failed\n");
return FALSE;
} else {
printf("deleted dsession verified\n");
return FALSE;
} else {
if (!verify_and_set("add", i)) {
return FALSE;
} else {
printf("added dsession %s verified\n",
}
}
}
}
return TRUE;
}
static Bool
sync_sessions(void)
{
int i, j, k;
return FALSE;
for (j = 0; j < num_sessions; j++) {
sizeof ("LoginWindow") - 1) == 0) &&
sizeof ("HotPlug") - 1) == 0) &&
/* handle closed sessions */
for (i = 0; i < disp_num; i++) {
ck_sessions[j].session_id) == 0) {
break;
}
}
if (display != -1) {
/* found a session in mgr table with valid display */
for (k = 0; k < num_sessions; k++) {
sizeof ("LoginWindow") - 1) != 0) &&
/*
* found user session (type is not "LoginWindow")
* with same display. update mgr table
*/
sizeof ("TRANSFERRED") - 1) != 0) {
"TRANSFERRED", TYPE_SIZE);
sizeof (dsession_mgr_t));
if (debug)
printf("session %s updated in sync\n",
}
break;
}
}
} else {
/* not a transferred session, delete it as it's closed */
"/usr/sbin/ck-seat-tool --delete --session-id=%s",
ck_sessions[j].session_id);
if (debug)
printf("session %s deleted in sync\n",
ck_sessions[j].session_id);
} else
if (i < disp_num) {
/* session in mgr table, need to update entry */
"Deleted", SESSION_SIZE);
"REMOVED", TYPE_SIZE);
sizeof (dsession_mgr_t));
}
}
}
}
for (i = 0; i < disp_num; i++) {
for (j = 0; j < num_sessions; j++) {
ck_sessions[j].session_id) == 0) &&
ck_sessions[j].open &&
== -1) {
/*
* corresponding session in ck table becomes open and
* the user session does not exist any more
*/
dsession_mgr_table[i].usr_session[0] = 0;
sizeof (dsession_mgr_t));
if (debug)
printf("session %s updated in sync\n",
break;
}
}
}
}
printf("sync sessions done\n");
return ret;
}
static Bool
fini(void)
{
int i;
IPC_ALLOC)) == -1) {
perror("shared memory get error");
return FALSE;
}
(void *) NULL, 0)) == (void *) -1) {
perror("shared memory attach error");
return FALSE;
}
mutex_lock(&lock);
for (i = 0; i < disp_num; i++) {
if (dsession_mgr_table[i].dev_path[0]) {
if (delete_session() == -1)
}
}
/* verify sessions just deleted */
if (debug)
printf("verifying the deleted dsessions ...\n");
printf("deleted dsessions all verified\n");
else
mutex_unlock(&lock);
perror("shared memory detach error");
return FALSE;
}
return TRUE;
}
static Bool
clean(void)
{
if (!fini())
IPC_ALLOC)) == -1) {
perror("shared memory get error");
return FALSE;
}
(void *) NULL, 0)) == (void *) -1) {
perror("shared memory attach error");
return FALSE;
}
perror("shared memory detach error");
return FALSE;
}
/* destroy the shared memory area */
perror("shared memory remove error");
return FALSE;
}
printf("shared memory removed\n");
return TRUE;
}
static Bool
init(void)
{
int i;
IPC_ALLOC)) != -1) {
/* shared memory already exists */
(void *) NULL, 0)) == (void *) -1) {
perror("shared memory attach error");
return FALSE;
}
mutex_lock(&lock);
for (i = 0; i < disp_num; i++) {
if (dsession_mgr_table[i].session_id[0] &&
"Deleted") != 0)) {
"before running \"dsession -i/--init\"\n");
mutex_unlock(&lock);
perror("shared memory detach error");
return FALSE;
}
}
shm_created = TRUE;
}
/* initialize mgr table */
if (pci_system_init() != 0) {
if (shm_created) {
mutex_unlock(&lock);
perror("shared memory detach error");
}
return FALSE;
}
disp_num = 0;
if (disp_num == MAX_DISP_DEV) {
if (shm_created) {
mutex_unlock(&lock);
perror("shared memory detach error");
}
return FALSE;
}
disp_num++;
}
}
}
if (!shm_created) {
/* shared memory has never been created */
perror("shared memory get error during create");
return FALSE;
}
printf("shared memory create successful\n");
(void *) NULL, 0)) == (void *) -1) {
perror("shared memory attach error");
return FALSE;
}
mutex_lock(&lock);
}
/* dynamically add sessions, one for each device */
for (i = 0; i < disp_num; i++) {
if (dsession_mgr_table[i].dev_path[0] &&
}
/* verify sessions just added */
if (debug)
printf("verifying the added dsessions ...\n");
else
mutex_unlock(&lock);
perror("shared memory detach error");
return FALSE;
}
return TRUE;
}
/*ARGSUSED*/
static void
{
int status;
if (debug)
mutex_unlock(&lock);
mutex_unlock(&lock);
perror("shared memory detach error");
exit(1);
}
static void
usage(void)
{
"\nUsage:\n");
"%s -h|--help\n%s", program_name,
" Print help messages\n\n");
"%s [debug-option] sub-command\n%s", program_name,
" Run sub-command, where\n\n"
" debug-option:\n"
" -v or --verb\t\tPrint debug messages\n\n"
" sub-command:\t\t(<dev> is path of display device)\n"
" -a or --add <dev>\t\tAdd a dsession to run on <dev>\n"
" -d or --delete <dev>\tDelete a dsession running on <dev>\n"
" -r or --restart <dev>\tRestart a dsession running on <dev>\n"
" -l or --list all\t\tList all dsessions\n"
" -l or --list dev\t\tProbe and list devs\n"
" -l or --list <dev>\t\tList dsession running on <dev>\n"
" -i or --init\t\tInitialize: create shared memory\n"
" \t\tand mutex, add all dsessions\n"
" -f or --fini\t\tFinish: delete all dsessions\n"
" -c or --clean\t\tDelete all dsessions,\n"
" \t\tdestroy mutex and shared memory\n");
}
static Bool
get_shm_key(void) {
return FALSE;
}
perror("IPC error: ftok");
return FALSE;
}
return TRUE;
}
int
{
program_name = argv[0];
int ret = 0;
SIGPIPE};
int i;
usage();
exit(0);
}
argc--;
argv++;
}
usage();
exit(1);
}
}
(argc == 3)) {
else
}
char *path;
} else {
exit(1);
}
}
if (!get_shm_key()) {
exit(1);
}
IPC_ALLOC)) == -1) {
perror("shared memory get error");
exit(1);
}
(void *) NULL, 0)) == (void *) -1) {
perror("shared memory attach error");
exit(1);
}
mutex_lock(&lock);
if (!sync_sessions())
if (add) {
ret = 1;
} else {
if (verify_and_set("add", i))
printf("added dsession %s verified\n",
else {
ret = 1;
}
}
} else if (delete) {
if ((i = delete_session()) == -1) {
ret = 1;
} else {
if (verify_and_set("delete", i))
printf("deleted dsession verified\n");
else {
ret = 1;
}
}
} else if (restart) {
if (restart_session())
printf("restart dsession on dev %s succeeded\n",
else {
ret = 1;
}
} else if (list_all) {
if (!print_mgr_table(TRUE))
} else if (list_dev) {
if (!print_mgr_table(FALSE)) {
ret = 1;
}
}
mutex_unlock(&lock);
perror("shared memory detach error");
exit(1);
}
} else if (argc == 2) {
if (!get_shm_key()) {
exit(1);
}
if (!init()) {
exit(1);
}
if (!fini()) {
exit(1);
}
if (!clean()) {
exit(1);
}
} else {
usage();
exit(1);
}
} else if (list_probe_dev) {
if (!probe_and_print_dev())
ret = 1;
} else {
usage();
exit(1);
}
/*NOTREACHED*/
}