add a gdm-control tool that lets you..control..gdm..
[mikachu/openbox.git] / tools / gdm-control / gdm-control.c
1 #include <string.h>
2 #include <stdio.h>
3 #include <assert.h>
4 #include <errno.h>
5 #include <unistd.h>
6 #include <sys/socket.h>
7 #include <sys/un.h>
8
9 #include <X11/Xlib.h>
10 #include <X11/Xauth.h>
11
12 #include <glib.h>
13
14 typedef enum
15 {
16     INVALID,
17     NONE,
18     SHUTDOWN,
19     REBOOT,
20     SUSPEND,
21     SWITCHUSER
22 } Action;
23
24 #define GDM_PROTOCOL_SOCKET_PATH1 "/var/run/gdm_socket"
25 #define GDM_PROTOCOL_SOCKET_PATH2 "/tmp/.gdm_socket"
26
27 #define GDM_PROTOCOL_MSG_CLOSE         "CLOSE"
28 #define GDM_PROTOCOL_MSG_VERSION       "VERSION"
29 #define GDM_PROTOCOL_MSG_AUTHENTICATE  "AUTH_LOCAL"
30 #define GDM_PROTOCOL_MSG_QUERY_ACTION  "QUERY_LOGOUT_ACTION"
31 #define GDM_PROTOCOL_MSG_SET_ACTION    "SET_SAFE_LOGOUT_ACTION"
32 #define GDM_PROTOCOL_MSG_FLEXI_XSERVER "FLEXI_XSERVER"
33
34 #define GDM_ACTION_STR_NONE     GDM_PROTOCOL_MSG_SET_ACTION" NONE"
35 #define GDM_ACTION_STR_SHUTDOWN GDM_PROTOCOL_MSG_SET_ACTION" HALT"
36 #define GDM_ACTION_STR_REBOOT   GDM_PROTOCOL_MSG_SET_ACTION" REBOOT"
37 #define GDM_ACTION_STR_SUSPEND  GDM_PROTOCOL_MSG_SET_ACTION" SUSPEND"
38
39 #define GDM_MIT_MAGIC_COOKIE_LEN 16
40
41 static int fd = 0;
42
43 static void gdm_disconnect()
44 {
45     if (fd > 0)
46         close(fd);
47     fd = 0;
48 }
49
50 static char* get_display_number(void)
51 {
52     char *display_name;
53     char *retval;
54     char *p;
55
56     display_name = XDisplayName(NULL);
57
58     p = strchr(display_name, ':');
59     if (!p)
60         return g_strdup ("0");
61
62     while (*p == ':') p++;
63
64     retval = g_strdup (p);
65
66     p = strchr (retval, '.');
67     if (p != NULL)
68         *p = '\0';
69
70     return retval;
71 }
72
73 static char* gdm_send_protocol_msg (const char *msg)
74 {
75     GString *retval;
76     char     buf[256];
77     char    *p;
78     int      len;
79
80     p = g_strconcat(msg, "\n", NULL);
81     if (write (fd, p, strlen(p)) < 0) {
82         g_free (p);
83
84         g_warning ("Failed to send message to GDM: %s",
85                    g_strerror (errno));
86         return NULL;
87     }
88     g_free (p);
89
90     p = NULL;
91     retval = NULL;
92     while ((len = read(fd, buf, sizeof(buf) - 1)) > 0) {
93         buf[len] = '\0';
94
95         if (!retval)
96             retval = g_string_new(buf);
97         else
98             retval = g_string_append(retval, buf);
99
100         if ((p = strchr(retval->str, '\n')))
101             break;
102     }
103
104     if (p) *p = '\0';
105
106     return retval ? g_string_free(retval, FALSE) : NULL;
107 }
108
109 static gboolean gdm_authenticate()
110 {
111     FILE       *f;
112     Xauth      *xau;
113     const char *xau_path;
114     char       *display_number;
115     gboolean    retval;
116
117     if (!(xau_path = XauFileName()))
118         return FALSE;
119
120     if (!(f = fopen(xau_path, "r")))
121         return FALSE;
122
123     retval = FALSE;
124     display_number = get_display_number();
125
126     while ((xau = XauReadAuth(f))) {
127         char  buffer[40]; /* 2*16 == 32, so 40 is enough */
128         char *msg;
129         char *response;
130         int   i;
131
132         if (xau->family != FamilyLocal ||
133             strncmp (xau->number, display_number, xau->number_length) ||
134             strncmp (xau->name, "MIT-MAGIC-COOKIE-1", xau->name_length) ||
135             xau->data_length != GDM_MIT_MAGIC_COOKIE_LEN)
136         {
137             XauDisposeAuth(xau);
138             continue;
139         }
140
141         for (i = 0; i < GDM_MIT_MAGIC_COOKIE_LEN; i++)
142             g_snprintf(buffer + 2*i, 3, "%02x", (guint)(guchar)xau->data[i]);
143
144         XauDisposeAuth(xau);
145
146         msg = g_strdup_printf(GDM_PROTOCOL_MSG_AUTHENTICATE " %s", buffer);
147         response = gdm_send_protocol_msg(msg);
148         g_free (msg);
149
150         if (response && !strcmp(response, "OK")) {
151             /*auth_cookie = g_strdup(buffer);*/
152             g_free(response);
153             retval = TRUE;
154             break;
155         }
156
157         g_free (response);
158     }
159
160     fclose(f);
161     return retval;
162 }
163
164 static gboolean gdm_connect()
165 {
166     struct sockaddr_un  addr;
167     char               *response;
168
169     assert(fd <= 0);
170
171     fd = socket(AF_UNIX, SOCK_STREAM, 0);
172     if (fd < 0) {
173         g_warning("Failed to create GDM socket: %s", g_strerror (errno));
174         gdm_disconnect();
175         return FALSE;
176     }
177
178     if (g_file_test(GDM_PROTOCOL_SOCKET_PATH1, G_FILE_TEST_EXISTS))
179         strcpy(addr.sun_path, GDM_PROTOCOL_SOCKET_PATH1);
180     else
181         strcpy(addr.sun_path, GDM_PROTOCOL_SOCKET_PATH2);
182
183     addr.sun_family = AF_UNIX;
184
185     if (connect(fd, (struct sockaddr *) &addr, sizeof (addr)) < 0) {
186         g_warning("Failed to establish a connection with GDM: %s",
187                   g_strerror(errno));
188         gdm_disconnect();
189         return FALSE;
190     }
191
192     response = gdm_send_protocol_msg(GDM_PROTOCOL_MSG_VERSION);
193     if (!response || strncmp(response, "GDM ", strlen("GDM ") != 0)) {
194         g_free(response);
195
196         g_warning("Failed to get protocol version from GDM");
197         gdm_disconnect();
198         return FALSE;
199     }
200     g_free(response);
201
202     if (!gdm_authenticate()) {
203         g_warning("Failed to authenticate with GDM");
204         gdm_disconnect();
205         return FALSE;
206     }
207
208     return TRUE;
209 }
210
211 int main(int argc, char **argv)
212 {
213     int i;
214     Action a = INVALID;
215
216     for (i = 1; i < argc; ++i) {
217         if (!strcmp(argv[i], "--help")) {
218             a = INVALID;
219             break;
220         }
221         if (!strcmp(argv[i], "--none")) {
222             a = NONE;
223             break;
224         }
225         if (!strcmp(argv[i], "--shutdown")) {
226             a = SHUTDOWN;
227             break;
228         }
229         if (!strcmp(argv[i], "--reboot")) {
230             a = REBOOT;
231             break;
232         }
233         if (!strcmp(argv[i], "--suspend")) {
234             a = SUSPEND;
235             break;
236         }
237         if (!strcmp(argv[i], "--switch-user")) {
238             a = SWITCHUSER;
239             break;
240         }
241     }
242
243     if (!a) {
244         printf("Usage: gdm-control ACTION\n\n");
245         printf("Actions:\n");
246         printf("    --help        Display this help and exit\n");
247         printf("    --none        Do nothing special when the current session ends\n");
248         printf("    --shutdown    Shutdown the computer when the current session ends\n");
249         printf("    --reboot      Reboot the computer when the current session ends\n");
250         printf("    --suspend     Suspend the computer when the current session ends\n");
251         printf("    --switch-user Log in as a new user (this works immediately)\n\n");
252         return 0;
253     }
254
255     {
256         char *d, *response;
257         const char *action_string;
258
259         d = XDisplayName(NULL);
260         if (!d) {
261             fprintf(stderr,
262                     "Unable to fina an X display specified by the DISPLAY "
263                     "environment variable. Ensure that it is set correctly.");
264             return 1;
265         }
266
267         switch (a) {
268         case NONE:
269             action_string = GDM_ACTION_STR_NONE;
270             break;
271         case SHUTDOWN:
272             action_string = GDM_ACTION_STR_SHUTDOWN;
273             break;
274         case REBOOT:
275             action_string = GDM_ACTION_STR_REBOOT;
276             break;
277         case SUSPEND:
278             action_string = GDM_ACTION_STR_SUSPEND;
279             break;
280         case SWITCHUSER:
281             action_string = GDM_PROTOCOL_MSG_FLEXI_XSERVER;
282             break;
283         default:
284             assert(0);
285         }
286
287         if (gdm_connect()) {
288             response = gdm_send_protocol_msg(action_string);
289             g_free(response);
290             gdm_disconnect();
291         }
292     }
293
294     return 0;
295 }