Stackdb
Stackdb is a stackable, multi-target and -level source debugger and memory forensics library.
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
target_xen_vm_vmp.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2014 The University of Utah
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of
7  * the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
17  */
18 
19 #include "config.h"
20 
21 #include <stdio.h>
22 #include <errno.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <sys/socket.h>
26 #include <sys/un.h>
27 #if !defined(UNIX_PATH_MAX)
28 #define UNIX_PATH_MAX (size_t)sizeof(((struct sockaddr_un *) 0)->sun_path)
29 #endif
30 #include <signal.h>
31 #include <getopt.h>
32 #include <unistd.h>
33 #include <stdlib.h>
34 #include <fcntl.h>
35 
36 #include <xenctrl.h>
37 #include <xen/xen.h>
38 
39 #include <glib.h>
40 
41 #include "common.h"
42 #include "target.h"
43 #include "target_xen_vm.h"
44 #include "target_xen_vm_vmp.h"
45 
46 extern char *optarg;
47 extern int optind, opterr, optopt;
48 
49 static int nodaemon = 0;
50 static GHashTable *clients = NULL;
51 static int sfd = -1;
52 char *path = NULL;
53 
54 #ifdef XENCTRL_HAS_XC_INTERFACE
55 static xc_interface *xc_handle = NULL;
56 static xc_interface *xce_handle = NULL;
57 #define XC_IF_INVALID (NULL)
58 #else
59 static int xc_handle = -1;
60 static int xce_handle = -1;
61 #define XC_IF_INVALID (-1)
62 #endif
63 
64 #if !defined(XC_EVTCHN_PORT_T)
65 #error "XC_EVTCHN_PORT_T undefined!"
66 #endif
67 static XC_EVTCHN_PORT_T dbg_port = -1;
68 
69 int xce_fd = -1;
70 
71 void cleanup() {
72  GHashTableIter iter;
73  gpointer kp,vp;
74 
75  if (path && path[0] != '\0') {
76  unlink(path);
77  path = NULL;
78  }
79  if (clients) {
80  g_hash_table_iter_init(&iter,clients);
81  while (g_hash_table_iter_next(&iter,&kp,&vp)) {
82  close((int)(uintptr_t)kp);
83  //free((void *)vp);
84  }
85  g_hash_table_destroy(clients);
86  clients = NULL;
87  }
88  if (sfd > -1) {
89  close(sfd);
90  sfd = -1;
91  }
92 
93  xen_vm_virq_detach(xce_handle,&dbg_port);
94  dbg_port = -1;
95  xen_vm_xc_detach(&xc_handle,&xce_handle);
96  xce_handle = XC_IF_INVALID;
98 }
99 
100 void sigh(int signo) {
101  cleanup();
102  if (signo == SIGHUP || signo == SIGINT || SIGQUIT)
103  exit(0);
104  else
105  exit(-100);
106 }
107 
108 int main(int argc,char **argv) {
109  int opt,rc;
110  struct stat sbuf;
111  struct sockaddr_un sun,sun_client;
112  socklen_t clen;
113  fd_set rfds,wfds,efds;
114  GHashTableIter iter;
115  gpointer kp,vp;
116  int max_fd,fd;
118  struct target_xen_vm_vmp_client_response resp = { 0 };
119  int len;
120  int port;
121  long int flags;
122 
123  while ((opt = getopt(argc,argv,"hd::w::l:p:")) > -1) {
124  switch (opt) {
125  case 'd':
126  nodaemon = 1;
127  if (optarg)
128  vmi_set_log_level(atoi(optarg));
129  else
131  break;
132  case 'w':
133  nodaemon = 1;
134  if (optarg)
135  vmi_set_warn_level(atoi(optarg));
136  else
138  break;
139  case 'l':
141  break;
142  case 'p':
143  path = optarg;
144  break;
145  case 'h':
146  case '?':
147  default:
148  verror("Usage: %s [-h] [-d [<level>]] [-w [<level>]]"
149  " [-l logflag1,logflag2,...]\n",argv[0]);
150  exit(-1);
151  }
152  }
153 
154  if (!nodaemon) {
155  if (daemon(0,0)) {
156  verror("daemon(): %s\n",strerror(errno));
157  exit(-1);
158  }
159  }
160 
161  signal(SIGHUP,sigh);
162  signal(SIGINT,sigh);
163  signal(SIGQUIT,sigh);
164  signal(SIGABRT,sigh);
165  signal(SIGSEGV,sigh);
166  //signal(SIGPIPE,sigh);
167 
168  signal(SIGALRM,SIG_IGN);
169  signal(SIGUSR1,SIG_IGN);
170  signal(SIGUSR2,SIG_IGN);
171 
172  clients = g_hash_table_new_full(g_direct_hash,g_direct_equal,NULL,NULL);
173 
174  if (!path) {
175  /*
176  * Just try /var/run or TMPDIR or /tmp or .
177  */
178  if (stat("/var/run",&sbuf) == 0
179  && S_ISDIR(sbuf.st_mode) && access("/var/run",W_OK) == 0)
180  path = "/var/run/" TARGET_XV_VMP_SOCKET_FILENAME;
181  else if ((path = getenv("TMPDIR"))
182  && stat(path,&sbuf) == 0 && access(path,W_OK) == 0) {
183  path = malloc(strlen(path) + strlen(TARGET_XV_VMP_SOCKET_FILENAME) + 2);
184  sprintf(path,"%s/%s",path,TARGET_XV_VMP_SOCKET_FILENAME);
185  }
186  else if (stat("/tmp",&sbuf) == 0
187  && S_ISDIR(sbuf.st_mode) && access("/tmp",W_OK) == 0)
189  else
191  }
192 
193  memset(&sun,0,sizeof(sun));
194  sun.sun_family = AF_UNIX;
195  snprintf(sun.sun_path,UNIX_PATH_MAX,"%s",path);
196 
197  sfd = socket(AF_UNIX,SOCK_STREAM,0);
198  if (sfd < 0) {
199  verror("socket(): %s\n",strerror(errno));
200  goto err;
201  }
202  len = offsetof(struct sockaddr_un,sun_path) + strlen(sun.sun_path);
203  if (bind(sfd,&sun,len) < 0) {
204  verror("bind(): %s\n",strerror(errno));
205  goto err;
206  }
207  if (fchmod(sfd,S_IRUSR | S_IWUSR) < 0) {
208  verror("chmod(%s): %s\n",sun.sun_path,strerror(errno));
209  goto err;
210  }
211  if (listen(sfd,8)) {
212  verror("listen(): %s\n",strerror(errno));
213  goto err;
214  }
215 
216  if (xen_vm_xc_attach(&xc_handle,&xce_handle)) {
217  verror("could not attach to XC interfaces!\n");
218  goto err;
219  }
220 
221  if (xen_vm_virq_attach(xce_handle,&dbg_port)) {
222  verror("could not attach to VIRQ_DEBUGGER evtchn!\n");
223  goto err;
224  }
225 
226  xce_fd = xc_evtchn_fd(xce_handle);
227 
228  vdebug(1,LA_TARGET,LF_XV,"Attached to Xen; listening for clients on %s...\n",
229  path);
230 
231  /*
232  * Main loop.
233  */
234  while (1) {
235  /*
236  * Reset all the FD info for select().
237  */
238  FD_ZERO(&rfds);
239  FD_ZERO(&wfds);
240  FD_ZERO(&efds);
241 
242  max_fd = sfd;
243  FD_SET(sfd,&rfds);
244 
245  if (xce_fd > max_fd)
246  max_fd = xce_fd;
247  FD_SET(xce_fd,&rfds);
248 
249  g_hash_table_iter_init(&iter,clients);
250  while (g_hash_table_iter_next(&iter,&kp,&vp)) {
251  fd = (int)(uintptr_t)kp;
252  if (fd > max_fd)
253  max_fd = fd;
254  FD_SET(fd,&rfds);
255  }
256 
257  /*
258  * Wait for
259  */
260  rc = select(max_fd + 1,&rfds,&wfds,&efds,NULL);
261  if (rc < 0) {
262  if (errno == EINTR)
263  continue;
264  else {
265  verror("select(): %s, exiting!\n",strerror(errno));
266  goto err;
267  }
268  }
269  else if (rc == 0)
270  continue;
271 
272  /*
273  * Check the read/except FDs first; remove any as clients if
274  * they've dropped.
275  */
276  g_hash_table_iter_init(&iter,clients);
277  while (g_hash_table_iter_next(&iter,&kp,&vp)) {
278  fd = (int)(uintptr_t)kp;
279  if (FD_ISSET(fd,&rfds) || FD_ISSET(fd,&efds)) {
280  rc = read(fd,&req,sizeof(req));
281  if (rc == 0) {
282  /* Remove this client. */
284  "Removing client %d (closed connection)\n",fd);
285  close(fd);
286  g_hash_table_iter_remove(&iter);
287  }
288  else if (rc == -1) {
289  if (errno == EINTR)
290  continue;
291  else {
292  /* Remove! */
294  "Removing client %d (read error: %s)\n",
295  fd,strerror(errno));
296  close(fd);
297  g_hash_table_iter_remove(&iter);
298  }
299  }
300  else if (rc != sizeof(req)) {
301  /* Remove */
303  "Removing client %d (bad request length: %d)\n",fd,rc);
304  close(fd);
305  g_hash_table_iter_remove(&iter);
306  }
307  else {
308  ;
309  }
310  }
311  }
312 
313  /*
314  * Check for new clients.
315  */
316  if (FD_ISSET(sfd,&rfds)) {
317  clen = sizeof(sun_client);
318  fd = accept(sfd,&sun_client,&clen);
319  if (fd > 0) {
320  clen -= offsetof(struct sockaddr_un,sun_path);
321  if (clen >= UNIX_PATH_MAX) {
322  vwarn("bad sockaddr_un.sun_path length from accept()!\n");
323  clen = UNIX_PATH_MAX - 1;
324  }
325 
326  if (clen < offsetof(struct sockaddr_un,sun_path)
327  || clen >= sizeof(sun_client.sun_path)) {
328  vwarn("ignoring new client with unbound unix socket!\n");
329  close(fd);
330  }
331  else {
332  sun_client.sun_path[clen] = '\0';
333  stat(sun_client.sun_path,&sbuf);
335  "accepting new client %d (pid %d, uid %d, path %s)\n",
336  fd,sbuf.st_rdev,sbuf.st_uid,sun_client.sun_path);
337  g_hash_table_insert(clients,(void *)(uintptr_t)fd,NULL);
338 
339  flags = fcntl(fd,F_GETFL);
340  fcntl(fd,F_SETFL,flags | O_NONBLOCK);
341  }
342  }
343  else {
344  verror("accept(): %s, ignoring\n",strerror(errno));
345  }
346  }
347 
348  /*
349  * Check the VIRQ event channel; forward to all for now.
350  *
351  * XXX: later, maybe, do the demultiplexing here and only
352  * forward to clients monitoring the VM in question.
353  */
354  if (FD_ISSET(xce_fd,&rfds)) {
355  /* we've got something from eventchn. let's see what it is! */
356  port = xc_evtchn_pending(xce_handle);
357 
358  /* unmask the event channel BEFORE doing anything else */
359  if (xc_evtchn_unmask(xce_handle,port) == -1) {
360  vwarn("failed to unmask event channel\n");
361  }
362 
363  if (port != dbg_port)
364  continue;
365 
366  g_hash_table_iter_init(&iter,clients);
367  while (g_hash_table_iter_next(&iter,&kp,&vp)) {
368  fd = (int)(uintptr_t)kp;
369  int tries = 3;
370  again:
371  if (tries == 0) {
372  vwarn("Removing client %d (write error: %s)\n",
373  fd,strerror(errno));
374  close(fd);
375  g_hash_table_iter_remove(&iter);
376  continue;
377  }
378 
379  rc = write(fd,&resp,sizeof(resp));
380 
381  if (rc < 0) {
382  if (errno == EINTR) {
383  --tries;
384  goto again;
385  }
386  else {
388  "Removing client %d (write error: %s)\n",
389  fd,strerror(errno));
390  close(fd);
391  g_hash_table_iter_remove(&iter);
392  }
393  }
394  else if (rc != sizeof(resp)) {
396  "Removing client %d (incomplete write: %d)\n",
397  fd,rc);
398  close(fd);
399  g_hash_table_iter_remove(&iter);
400  }
401  else {
403  "Wrote %d bytes to client %d\n",rc,fd);
404  }
405  }
406  }
407  }
408 
409  err:
410  cleanup();
411  exit(-1);
412 }
#define TARGET_XV_VMP_SOCKET_FILENAME
int xen_vm_virq_attach(int xce_handle, XC_EVTCHN_PORT_T *dbg_port)
void sigh(int signo)
void vmi_inc_log_level(void)
Definition: log.c:38
char * path
char * optarg
#define verror(format,...)
Definition: log.h:30
void cleanup()
void vmi_set_log_level(int level)
Definition: log.c:34
#define vwarn(format,...)
Definition: log.h:33
#define XC_IF_INVALID
int xc_handle
#define UNIX_PATH_MAX
int main(int argc, char **argv)
int vmi_set_log_area_flaglist(char *flaglist, char *separator)
Definition: log.c:84
int xce_fd
void vmi_set_warn_level(int level)
Definition: log.c:42
int len
Definition: dumptarget.c:52
void vmi_inc_warn_level(void)
Definition: log.c:46
int opterr
#define vdebug(devel, areas, flags, format,...)
Definition: log.h:302
int optind
Definition: log.h:172
int unlink(const char *pathname)
Definition: qemuhacks.c:134
Definition: log.h:70
int xen_vm_virq_detach(int xce_handle, XC_EVTCHN_PORT_T *dbg_port)
int xen_vm_xc_detach(int *xc_handle, int *xce_handle)
void * malloc(size_t size)
Definition: debugserver.c:214
int optopt
int xen_vm_xc_attach(int *xc_handle, int *xce_handle)