A tiling window manager
1/*
2 * communications.c -- Send commands to a running copy of sdorfehs.
3 * Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts <sabetts@vcn.bc.ca>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation; either version 2 of the License, or (at your option)
8 * any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14 *
15 * You should have received a copy of the GNU General Public License along with
16 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
17 * Place, Suite 330, Boston, MA 02111-1307 USA.
18 */
19
20#include <X11/X.h>
21#include <X11/Xlib.h>
22#include <X11/Xutil.h>
23#include <X11/Xatom.h>
24#include <X11/Xproto.h>
25
26#include <sys/socket.h>
27#include <sys/un.h>
28#include <fcntl.h>
29#include <string.h>
30#include <unistd.h>
31#include <err.h>
32#include <errno.h>
33#include <stdlib.h>
34
35#include "sdorfehs.h"
36
37#define BUFSZ 1024
38
39void
40init_control_socket_path(void)
41{
42 char *config_dir;
43
44 config_dir = get_config_dir();
45 rp_glob_screen.control_socket_path = xsprintf("%s/control", config_dir);
46 free(config_dir);
47}
48
49void
50listen_for_commands(void)
51{
52 struct sockaddr_un sun;
53
54 if ((rp_glob_screen.control_socket_fd = socket(AF_UNIX,
55 SOCK_STREAM | SOCK_NONBLOCK, 0)) == -1)
56 err(1, "socket");
57
58 if (strlen(rp_glob_screen.control_socket_path) >= sizeof(sun.sun_path))
59 err(1, "control socket path too long: %s",
60 rp_glob_screen.control_socket_path);
61
62 strncpy(sun.sun_path, rp_glob_screen.control_socket_path,
63 sizeof(sun.sun_path));
64 sun.sun_family = AF_UNIX;
65
66 if (unlink(rp_glob_screen.control_socket_path) == -1 &&
67 errno != ENOENT)
68 err(1, "unlink %s",rp_glob_screen.control_socket_path);
69
70 if (bind(rp_glob_screen.control_socket_fd, (struct sockaddr *)&sun,
71 sizeof(sun)) == -1)
72 err(1, "bind %s", rp_glob_screen.control_socket_path);
73
74 if (chmod(rp_glob_screen.control_socket_path, 0600) == -1)
75 err(1, "chmod %s", rp_glob_screen.control_socket_path);
76
77 if (listen(rp_glob_screen.control_socket_fd, 2) == -1)
78 err(1, "listen %s", rp_glob_screen.control_socket_path);
79
80 PRINT_DEBUG(("listening for commands at %s\n",
81 rp_glob_screen.control_socket_path));
82}
83
84int
85send_command(int interactive, unsigned char *cmd)
86{
87 struct sockaddr_un sun;
88 char *wcmd, *bufstart;
89 char ret[BUFSZ+1];
90 char success = 0;
91 size_t len;
92 ssize_t count;
93 int fd, firstloop;
94 int flags = 0x0;
95 FILE *outf = NULL;
96
97#ifdef SENDCMD_DEBUG
98 pid_t pid = getpid();
99 char *dpfx = xsprintf("send_command_%d", pid);
100#endif
101 WARNX_DEBUG("%s: enter\n", dpfx);
102
103 len = 1 + strlen((char *)cmd) + 2;
104 wcmd = malloc(len);
105 if (snprintf(wcmd, len, "%c%s\n", interactive, cmd) != (len - 1))
106 errx(1, "snprintf");
107
108 if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
109 err(1, "socket");
110
111 if (strlen(rp_glob_screen.control_socket_path) >= sizeof(sun.sun_path))
112 err(1, "control socket path too long: %s",
113 rp_glob_screen.control_socket_path);
114
115 strncpy(sun.sun_path, rp_glob_screen.control_socket_path,
116 sizeof(sun.sun_path));
117 sun.sun_family = AF_UNIX;
118
119 if (connect(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1)
120 err(1, "failed to connect to control socket at %s",
121 rp_glob_screen.control_socket_path);
122
123 if (write(fd, wcmd, len) != len)
124 err(1, "short write to control socket");
125
126 free(wcmd);
127
128 firstloop = 1;
129 while ((count = recv(fd, &ret, BUFSZ, flags))) {
130 bufstart = ret;
131 if (firstloop) {
132 WARNX_DEBUG("%s: first recv: %zu\n", dpfx, count);
133 /* first byte is exit status */
134 success = *ret;
135 outf = success ? stdout : stderr;
136 bufstart++;
137 if (count == 2 && *bufstart == '\n')
138 /* commands that had no output */
139 return success;
140 /*
141 * after blocking for the first buffer, we can keep
142 * reading until it blocks again, which should exhaust
143 * the response. don't want to block when the message
144 * is finished: sometimes connection is closed, other
145 * times it blocks, not sure why? both end a response
146 */
147 flags += MSG_DONTWAIT;
148 }
149 if (count == -1) {
150 WARNX_DEBUG("%s: finish errno: %d\n", dpfx, errno);
151 if (errno == EAGAIN || errno == ECONNRESET)
152 return success;
153 }
154 ret[count] = '\0';
155 fprintf(outf, "%s", bufstart);
156 fflush(outf);
157 firstloop = 0;
158 WARNX_DEBUG("%s: looping\n", dpfx);
159 }
160 WARNX_DEBUG("%s: no more bytes\n", dpfx);
161#ifdef SENDCMD_DEBUG
162 free(dpfx);
163#endif
164
165 return success;
166}
167
168void
169receive_command(void)
170{
171 cmdret *cmd_ret;
172 char cmd[BUFSZ] = { 0 }, c;
173 char *result, *rcmd;
174 int cl, len = 0, interactive = 0;
175
176 PRINT_DEBUG(("have connection waiting on command socket\n"));
177
178 if ((cl = accept(rp_glob_screen.control_socket_fd, NULL, NULL)) == -1) {
179 warn("accept");
180 return;
181 }
182
183 while (len <= sizeof(cmd)) {
184 if (len == sizeof(cmd)) {
185 warn("%s: bogus command length", __func__);
186 close(cl);
187 return;
188 }
189
190 if (read(cl, &c, 1) == 1) {
191 if (c == '\n') {
192 cmd[len++] = '\0';
193 break;
194 }
195 cmd[len++] = c;
196 } else if (errno != EAGAIN) {
197 PRINT_DEBUG(("bad read result on control socket: %s\n",
198 strerror(errno)));
199 break;
200 }
201 }
202
203 interactive = cmd[0];
204 rcmd = cmd + 1;
205
206 PRINT_DEBUG(("read %d byte(s) on command socket: %s\n", len, rcmd));
207
208 cmd_ret = command(interactive, rcmd);
209
210 /* notify the client of any text that was returned by the command */
211 len = 2;
212 if (cmd_ret->output) {
213 result = xsprintf("%c%s\n", cmd_ret->success ? 1 : 0,
214 cmd_ret->output);
215 len = 1 + strlen(cmd_ret->output) + 1;
216 } else if (cmd_ret->success)
217 result = xsprintf("%c\n", 1);
218 else
219 result = xsprintf("%c\n", 0);
220
221 cmdret_free(cmd_ret);
222
223 PRINT_DEBUG(("writing back %d to command client: %s", len, result + 1));
224
225 if (write(cl, result, len) != len)
226 warn("%s: short write", __func__);
227
228 PRINT_DEBUG(("receive_command: write finished, closing\n"));
229
230 close(cl);
231}