Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 
 

432 строки
13 KiB

  1. /*
  2. * neercs console-based window manager
  3. * Copyright (c) 2006-2010 Sam Hocevar <sam@hocevar.net>
  4. * 2008-2010 Jean-Yves Lamoureux <jylam@lnxscene.org>
  5. * 2008-2010 Pascal Terjan <pterjan@linuxfr.org>
  6. * All Rights Reserved
  7. *
  8. * This program is free software. It comes without any warranty, to
  9. * the extent permitted by applicable law. You can redistribute it
  10. * and/or modify it under the terms of the Do What The Fuck You Want
  11. * To Public License, Version 2, as published by Sam Hocevar. See
  12. * http://www.wtfpl.net/ for more details.
  13. */
  14. #if defined HAVE_CONFIG_H
  15. # include "config.h"
  16. #endif
  17. #if !defined _WIN32 && defined HAVE_GLOB_H
  18. #include <errno.h>
  19. #include <fcntl.h>
  20. #include <glob.h>
  21. #include <limits.h>
  22. #include <stdio.h>
  23. #include <stdlib.h>
  24. #include <unistd.h>
  25. #include <sys/socket.h>
  26. #include <sys/un.h>
  27. #include <sys/types.h>
  28. #include <sys/stat.h>
  29. #include <caca.h>
  30. #include "neercs.h"
  31. char *build_socket_path(char *socket_dir, char *session_name,
  32. enum socket_type socktype)
  33. {
  34. char *path, *dir;
  35. path = (char *)malloc(PATH_MAX + 1);
  36. dir = socket_dir;
  37. if (!dir)
  38. dir = getenv("NEERCSDIR");
  39. if (!dir)
  40. dir = getenv("TMPDIR");
  41. if (!dir)
  42. dir = "/tmp";
  43. if (path)
  44. snprintf(path, PATH_MAX + 1, "%s/neercs.%s%s.sock", dir, session_name,
  45. socktype ? "" : ".srv");
  46. return path;
  47. }
  48. static char *socket_to_session(char const *sockpath)
  49. {
  50. char *p, *s;
  51. p = strrchr(sockpath, '/');
  52. if (!p)
  53. {
  54. debug("Invalid socket path %s", sockpath);
  55. return NULL;
  56. }
  57. p += 8; /* skip neercs. */
  58. s = strdup(p);
  59. p = strrchr(s, '.');
  60. *p = '\0'; /* drop .sock */
  61. p = strrchr(s, '.');
  62. *p = '\0'; /* drop .srv */
  63. p = strdup(s);
  64. free(s);
  65. return p;
  66. }
  67. int create_socket(struct screen_list *screen_list, enum socket_type socktype)
  68. {
  69. int sock;
  70. struct sockaddr_un myaddr;
  71. sock = socket(AF_UNIX, SOCK_DGRAM, 0);
  72. if (sock < 0)
  73. {
  74. perror("create_socket:socket");
  75. return errno;
  76. }
  77. memset(&myaddr, 0, sizeof(struct sockaddr_un));
  78. myaddr.sun_family = AF_UNIX;
  79. strncpy(myaddr.sun_path, screen_list->comm.socket_path[socktype],
  80. sizeof(myaddr.sun_path) - 1);
  81. unlink(screen_list->comm.socket_path[socktype]);
  82. if (bind(sock, (struct sockaddr *)&myaddr, sizeof(struct sockaddr_un)) < 0)
  83. {
  84. free(screen_list->comm.socket_path[socktype]);
  85. screen_list->comm.socket_path[socktype] = NULL;
  86. close(sock);
  87. perror("create_socket:bind");
  88. return errno;
  89. }
  90. fcntl(sock, F_SETFL, O_NONBLOCK);
  91. debug("Listening on %s (%d)", screen_list->comm.socket_path[socktype], sock);
  92. screen_list->comm.socket[socktype] = sock;
  93. return 0;
  94. }
  95. char **list_sockets(char *socket_dir, char *session_name)
  96. {
  97. char *pattern, *dir;
  98. glob_t globbuf;
  99. globbuf.gl_pathv = NULL;
  100. pattern = (char *)malloc(PATH_MAX + 1);
  101. dir = socket_dir;
  102. if (!dir)
  103. dir = getenv("NEERCSDIR");
  104. if (!dir)
  105. dir = getenv("TMPDIR");
  106. if (!dir)
  107. dir = "/tmp";
  108. if (!pattern)
  109. return globbuf.gl_pathv;
  110. if (session_name && strlen(session_name) + strlen(dir) + 13 < PATH_MAX)
  111. sprintf(pattern, "%s/neercs.%s.srv.sock", dir, session_name);
  112. else
  113. snprintf(pattern, PATH_MAX, "%s/neercs.*.srv.sock", dir);
  114. pattern[PATH_MAX] = '\0';
  115. debug("Looking for sockets in the form %s", pattern);
  116. glob(pattern, 0, NULL, &globbuf);
  117. free(pattern);
  118. return globbuf.gl_pathv;
  119. }
  120. char *connect_socket(struct screen_list *screen_list,
  121. enum socket_type socktype)
  122. {
  123. int sock;
  124. struct sockaddr_un addr;
  125. debug("Connecting to %s", screen_list->comm.socket_path[socktype]);
  126. /* Open the socket */
  127. if ((sock = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0)
  128. {
  129. debug("Failed to create socket\n");
  130. perror("connect_server:socket");
  131. return NULL;
  132. }
  133. memset(&addr, 0, sizeof(addr));
  134. addr.sun_family = AF_UNIX;
  135. strcpy(addr.sun_path, screen_list->comm.socket_path[socktype]);
  136. if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0)
  137. {
  138. debug("Failed to connect to %s: %s",
  139. screen_list->comm.socket_path[socktype], strerror(errno));
  140. close(sock);
  141. return NULL;
  142. }
  143. fcntl(sock, F_SETFL, O_NONBLOCK);
  144. screen_list->comm.socket[socktype] = sock;
  145. if (socktype == SOCK_SERVER)
  146. {
  147. return socket_to_session(screen_list->comm.socket_path[socktype]);
  148. }
  149. else
  150. return NULL;
  151. }
  152. int request_attach(struct screen_list *screen_list)
  153. {
  154. char buf[41];
  155. int bytes;
  156. bytes = snprintf(buf, sizeof(buf) - 1, "ATTACH %10d %10d %10d",
  157. caca_get_canvas_width(screen_list->cv),
  158. caca_get_canvas_height(screen_list->cv),
  159. screen_list->delay);
  160. buf[bytes] = '\0';
  161. debug("Requesting attach: %s", buf);
  162. return write(screen_list->comm.socket[SOCK_SERVER], buf, strlen(buf)) <= 0;
  163. }
  164. static char *select_socket(struct screen_list *screen_list)
  165. {
  166. char **sockets = NULL, **usable_sockets = NULL;
  167. int i, sock, nb_usable_sockets = 0;
  168. char *ret = NULL;
  169. sockets = list_sockets(screen_list->comm.socket_dir, screen_list->comm.session_name);
  170. if (sockets)
  171. {
  172. for (i = 0; sockets[i]; i++);
  173. /* Return the socket or NULL if there is not more than one match */
  174. if (i <= 1)
  175. {
  176. if (sockets[0])
  177. ret = strdup(sockets[0]);
  178. goto end;
  179. }
  180. /* Else ask the user to chose one */
  181. usable_sockets = malloc(i * sizeof(char *));
  182. for (i = 0; sockets[i]; i++)
  183. {
  184. struct sockaddr_un addr;
  185. if ((sock = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0)
  186. {
  187. perror("select_socket:socket");
  188. goto end;
  189. }
  190. memset(&addr, 0, sizeof(addr));
  191. addr.sun_family = AF_UNIX;
  192. strcpy(addr.sun_path, sockets[i]);
  193. if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0)
  194. {
  195. switch (errno)
  196. {
  197. case EACCES:
  198. debug("Connection refused on %s", sockets[i]);
  199. break;
  200. case ECONNREFUSED:
  201. fprintf(stderr, "%s is dead, removing\n", sockets[i]);
  202. unlink(sockets[i]);
  203. break;
  204. default:
  205. fprintf(stderr, "Unknown error on %s:%s\n", sockets[i],
  206. strerror(errno));
  207. }
  208. }
  209. else
  210. {
  211. usable_sockets[nb_usable_sockets] = strdup(sockets[i]);
  212. debug("%s is usable", usable_sockets[nb_usable_sockets]);
  213. nb_usable_sockets++;
  214. close(sock);
  215. }
  216. }
  217. if (!nb_usable_sockets)
  218. goto end;
  219. if (nb_usable_sockets == 1)
  220. {
  221. ret = strdup(usable_sockets[0]);
  222. goto end;
  223. }
  224. else
  225. {
  226. caca_event_t ev;
  227. enum caca_event_type t;
  228. int current_line = 1;
  229. int refresh = 1;
  230. screen_list->cv = caca_create_canvas(0, 0);
  231. screen_list->dp = caca_create_display(screen_list->cv);
  232. if (!screen_list->dp)
  233. goto end;
  234. caca_set_cursor(screen_list->dp, 0);
  235. caca_set_display_title(screen_list->dp, PACKAGE_STRING);
  236. while (1)
  237. {
  238. if (refresh)
  239. {
  240. caca_set_color_ansi(screen_list->cv, CACA_BLUE, CACA_BLUE);
  241. caca_fill_box(screen_list->cv,
  242. 0, 0,
  243. caca_get_canvas_width(screen_list->cv),
  244. caca_get_canvas_height(screen_list->cv),
  245. '#');
  246. caca_set_color_ansi(screen_list->cv, CACA_DEFAULT,
  247. CACA_BLUE);
  248. caca_draw_cp437_box(screen_list->cv, 0, 0,
  249. caca_get_canvas_width(screen_list->cv),
  250. caca_get_canvas_height(screen_list->
  251. cv));
  252. caca_printf(screen_list->cv, 2, 2,
  253. "Please select a session to reconnect:");
  254. for (i = 0; i < nb_usable_sockets; i++)
  255. {
  256. if (i == current_line - 1)
  257. {
  258. caca_set_attr(screen_list->cv, CACA_BOLD);
  259. caca_set_color_ansi(screen_list->cv, CACA_GREEN,
  260. CACA_BLUE);
  261. caca_put_char(screen_list->cv, 1, i + 3, '>');
  262. }
  263. else
  264. {
  265. caca_set_attr(screen_list->cv, 0);
  266. caca_set_color_ansi(screen_list->cv,
  267. CACA_LIGHTGRAY, CACA_BLUE);
  268. caca_put_char(screen_list->cv, 1, i + 3, ' ');
  269. }
  270. caca_printf(screen_list->cv,
  271. 3, i + 3,
  272. "%s",
  273. socket_to_session(usable_sockets[i]));
  274. }
  275. caca_refresh_display(screen_list->dp);
  276. refresh = 0;
  277. }
  278. if (!caca_get_event(screen_list->dp,
  279. CACA_EVENT_KEY_PRESS
  280. | CACA_EVENT_RESIZE
  281. | CACA_EVENT_QUIT, &ev, 10000))
  282. continue;
  283. t = caca_get_event_type(&ev);
  284. if (t & CACA_EVENT_KEY_PRESS)
  285. {
  286. unsigned int c = caca_get_event_key_ch(&ev);
  287. switch (c)
  288. {
  289. case CACA_KEY_UP:
  290. if (current_line > 1)
  291. current_line--;
  292. break;
  293. case CACA_KEY_DOWN:
  294. if (current_line < nb_usable_sockets)
  295. current_line++;
  296. break;
  297. case CACA_KEY_RETURN:
  298. ret = strdup(usable_sockets[current_line - 1]);
  299. goto end;
  300. break;
  301. case CACA_KEY_ESCAPE:
  302. goto end;
  303. break;
  304. default:
  305. break;
  306. }
  307. refresh = 1;
  308. }
  309. else if (t & CACA_EVENT_RESIZE)
  310. {
  311. refresh = 1;
  312. }
  313. else if (t & CACA_EVENT_QUIT)
  314. goto end;
  315. }
  316. }
  317. }
  318. end:
  319. if (sockets)
  320. {
  321. for (i = 0; sockets[i]; i++)
  322. free(sockets[i]);
  323. free(sockets);
  324. }
  325. if (usable_sockets)
  326. {
  327. for (i = 0; i < nb_usable_sockets; i++)
  328. free(usable_sockets[i]);
  329. free(usable_sockets);
  330. }
  331. if (screen_list->dp)
  332. {
  333. caca_free_display(screen_list->dp);
  334. screen_list->dp = NULL;
  335. }
  336. if (screen_list->cv)
  337. {
  338. caca_free_canvas(screen_list->cv);
  339. screen_list->cv = NULL;
  340. }
  341. return ret;
  342. }
  343. void attach(struct screen_list *screen_list)
  344. {
  345. screen_list->comm.socket_path[SOCK_SERVER] = select_socket(screen_list);
  346. if (screen_list->comm.socket_path[SOCK_SERVER])
  347. {
  348. char *session;
  349. session = connect_socket(screen_list, SOCK_SERVER);
  350. if (session)
  351. {
  352. debug("Connected to session %s", session);
  353. /* Create main canvas and associated caca window */
  354. screen_list->cv = caca_create_canvas(0, 0);
  355. screen_list->dp = caca_create_display(screen_list->cv);
  356. if (!screen_list->dp)
  357. return;
  358. caca_set_display_time(screen_list->dp, screen_list->delay * 1000);
  359. caca_set_cursor(screen_list->dp, 1);
  360. screen_list->comm.socket_path[SOCK_CLIENT] =
  361. build_socket_path(screen_list->comm.socket_dir, session,
  362. SOCK_CLIENT);
  363. create_socket(screen_list, SOCK_CLIENT);
  364. request_attach(screen_list);
  365. if (screen_list->comm.session_name)
  366. free(screen_list->comm.session_name);
  367. screen_list->comm.session_name = session;
  368. }
  369. else
  370. {
  371. fprintf(stderr, "Failed to attach!\n");
  372. free(screen_list->comm.socket_path[SOCK_SERVER]);
  373. screen_list->comm.socket_path[SOCK_SERVER] = NULL;
  374. screen_list->sys.attach = 0;
  375. }
  376. }
  377. else
  378. {
  379. fprintf(stderr, "No socket found!\n");
  380. screen_list->sys.attach = 0;
  381. }
  382. }
  383. #endif