mssh.c 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <getopt.h>
  5. #include <errno.h>
  6. #include <gtk/gtk.h>
  7. #include "config.h"
  8. #include "mssh-window.h"
  9. #define CONFFILE ".mssh_clusters"
  10. #define PKGINFO PACKAGE_NAME " " VERSION
  11. #define COPYRIGHT "Copyright (C) 2009 Bradley Smith <[email protected]>"
  12. static void on_mssh_destroy(GtkWidget *widget, gpointer data)
  13. {
  14. gtk_widget_hide(widget);
  15. gtk_main_quit();
  16. }
  17. void usage(const char *argv0)
  18. {
  19. fprintf(stderr, "%s\n", PKGINFO);
  20. fprintf(stderr, "%s\n", COPYRIGHT);
  21. fprintf(stderr, "An ssh client to issue the same commands to multiple servers\n\n");
  22. fprintf(stderr, "Usage: %s [OPTION]... (-a ALIAS | HOSTS)\n\n",
  23. argv0);
  24. fprintf(stderr,
  25. " -a, --alias=ALIAS Open hosts associated with named alias\n");
  26. fprintf(stderr,
  27. " -c, --columns=NUM Override gconf for number of columns\n");
  28. fprintf(stderr,
  29. " -h, --help Display this help and exit\n");
  30. fprintf(stderr,
  31. " -V, --version Output version information and exit\n");
  32. fprintf(stderr, "\nReport bugs to <%s>.\n", PACKAGE_BUGREPORT);
  33. exit(EXIT_FAILURE);
  34. }
  35. static char *fgetline(FILE *stream)
  36. {
  37. size_t len = 64;
  38. size_t pos = 0;
  39. char c;
  40. char *buf;
  41. if((buf = malloc(len)) == NULL)
  42. {
  43. perror("malloc");
  44. exit(EXIT_FAILURE);
  45. }
  46. while((c = fgetc(stream)) != EOF)
  47. {
  48. if(pos >= len)
  49. {
  50. len *= 2;
  51. if((buf = realloc(buf, len)) == NULL)
  52. {
  53. perror("realloc");
  54. exit(EXIT_FAILURE);
  55. }
  56. }
  57. if(c == '\n')
  58. {
  59. buf[pos++] = '\0';
  60. break;
  61. }
  62. else
  63. {
  64. buf[pos++] = c;
  65. }
  66. }
  67. if(c == EOF)
  68. {
  69. free(buf);
  70. return NULL;
  71. }
  72. return buf;
  73. }
  74. void append_alias(char *alias, GArray *hosts, GData **aliases, int lineno)
  75. {
  76. int i;
  77. GArray *fetched;
  78. if((fetched = g_datalist_get_data(aliases, alias)) == NULL)
  79. {
  80. printf("Line %d: Alias '%s' not defined\n", lineno, alias);
  81. exit(EXIT_FAILURE);
  82. }
  83. for(i = 0; i < fetched->len; i++)
  84. {
  85. g_array_append_val(hosts, g_array_index(fetched, char*, i));
  86. }
  87. }
  88. GData **parse_aliases(char *conffile)
  89. {
  90. FILE *file;
  91. char *line;
  92. int lineno = 0;
  93. GData **aliases = malloc(sizeof(GData*));
  94. g_datalist_init(aliases);
  95. if((file = fopen(conffile, "r")) == NULL)
  96. return aliases;
  97. while((line = fgetline(file)) != NULL)
  98. {
  99. char *sep, *alias, *hoststr, *tmp;
  100. GArray *hosts;
  101. lineno++;
  102. if(strcmp(line, "") == 0)
  103. continue;
  104. if(*line == '#')
  105. continue;
  106. if(*line == '{')
  107. continue;
  108. if((sep = strchr(line, ':')) == NULL)
  109. {
  110. printf("Line %d: Failed to parse line '%s'\n", lineno, line);
  111. exit(EXIT_FAILURE);
  112. }
  113. *sep = '\0';
  114. alias = line;
  115. hoststr = sep + 1;
  116. if((tmp = strtok(hoststr, " ")) == NULL)
  117. {
  118. printf("Line %d: Alias '%s' specifies no hosts\n", lineno,
  119. alias);
  120. exit(EXIT_FAILURE);
  121. }
  122. hosts = g_array_new(FALSE, TRUE, sizeof(char*));
  123. do
  124. {
  125. if(tmp[0] == '[' && tmp[strlen(tmp) - 1] == ']')
  126. {
  127. tmp++;
  128. tmp[strlen(tmp) - 1] = '\0';
  129. append_alias(tmp, hosts, aliases, lineno);
  130. }
  131. else
  132. g_array_append_val(hosts, tmp);
  133. }
  134. while((tmp = strtok(NULL, " ")) != NULL);
  135. g_datalist_set_data(aliases, alias, hosts);
  136. }
  137. return aliases;
  138. }
  139. GData **parse_commands(char *conffile)
  140. {
  141. FILE *file;
  142. char *line;
  143. int lineno = 0;
  144. GData **commands = malloc(sizeof(GData*));
  145. g_datalist_init(commands);
  146. if((file = fopen(conffile, "r")) == NULL)
  147. return commands;
  148. while((line = fgetline(file)) != NULL)
  149. {
  150. char *sep, *command, *commandline;
  151. lineno++;
  152. if(strcmp(line, "") == 0)
  153. continue;
  154. if(*line == '#')
  155. continue;
  156. if(*line != '{')
  157. continue;
  158. if((sep = strchr(line, '}')) == NULL)
  159. {
  160. printf("Line %d: Failed to parse line '%s'\n", lineno, line);
  161. exit(EXIT_FAILURE);
  162. }
  163. *sep = '\0';
  164. command = line + 1;
  165. if((commandline = strtok(sep + 1, " ")) == NULL)
  166. {
  167. printf("Line %d: Command Alias '%s' specifies no command\n", lineno,
  168. command);
  169. exit(EXIT_FAILURE);
  170. }
  171. g_datalist_set_data(commands, command, commandline);
  172. }
  173. return commands;
  174. }
  175. int main(int argc, char* argv[], char* env[])
  176. {
  177. GtkWidget* window;
  178. int c, option_index = 0;
  179. char *home, *conffile;
  180. long cols = 0;
  181. GData **aliases = NULL;
  182. GData **commands = NULL;
  183. GArray *hosts = NULL;
  184. static struct option long_options[] =
  185. {
  186. {"alias", required_argument, 0, 'a'},
  187. {"columns", required_argument, 0, 'c'},
  188. {"help", no_argument, 0, 'h'},
  189. {"version", no_argument, 0, 'V'},
  190. {0, 0, 0, 0}
  191. };
  192. if((home = getenv("HOME")) != NULL)
  193. {
  194. int len = strlen(home) + strlen(CONFFILE) + 2;
  195. conffile = malloc(len);
  196. snprintf(conffile, len, "%s/%s", home, CONFFILE);
  197. aliases = parse_aliases(conffile);
  198. commands = parse_commands(conffile);
  199. free(conffile);
  200. }
  201. else
  202. {
  203. fprintf(stderr,
  204. "Warning: $HOME not set, not reading config file\n");
  205. }
  206. for(;;)
  207. {
  208. c = getopt_long(argc, argv, "a:c:hV", long_options, &option_index);
  209. if(c == -1)
  210. break;
  211. switch(c)
  212. {
  213. case 'a':
  214. if(aliases && (hosts = g_datalist_get_data(aliases,
  215. optarg)) == NULL)
  216. {
  217. fprintf(stderr, "Alias '%s' not found\n\n", optarg);
  218. usage(argv[0]);
  219. }
  220. break;
  221. case 'c':
  222. errno = 0;
  223. cols = strtol(optarg, NULL, 10);
  224. if(cols <= 0 || errno != 0)
  225. {
  226. fprintf(stderr, "Invalid number of columns '%s'\n\n",
  227. optarg);
  228. usage(argv[0]);
  229. }
  230. break;
  231. case 'h':
  232. usage(argv[0]);
  233. break;
  234. case 'V':
  235. printf("%s\n\n", PKGINFO);
  236. printf("%s\n\n", COPYRIGHT);
  237. printf("Redistribution and use in source and binary forms, with or without\n");
  238. printf("modification, are permitted provided that the following conditions are met:\n");
  239. printf("\n");
  240. printf(" 1. Redistributions of source code must retain the copyright notice,\n");
  241. printf(" this list of conditions and the following disclaimer.\n");
  242. printf(" 2. Redistributions in binary form must reproduce the copyright notice,\n");
  243. printf(" this list of conditions and the following disclaimer in the\n");
  244. printf(" documentation and/or other materials provided with the distribution.\n");
  245. printf(" 3. The name of the author may not be used to endorse or promote\n");
  246. printf(" products derived from this software without specific prior written\n");
  247. printf(" permission.\n");
  248. printf("\n");
  249. printf("THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n");
  250. printf("IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n");
  251. printf("OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN\n");
  252. printf("NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n");
  253. printf("SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\n");
  254. printf("TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n");
  255. printf("PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n");
  256. printf("LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n");
  257. printf("NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n");
  258. printf("SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n");
  259. exit(EXIT_SUCCESS);
  260. break;
  261. case '?':
  262. printf("\n");
  263. usage(argv[0]);
  264. exit(EXIT_FAILURE);
  265. break;
  266. default:
  267. abort();
  268. }
  269. }
  270. if(hosts == NULL)
  271. {
  272. hosts = g_array_new(FALSE, TRUE, sizeof(char*));
  273. if (optind < argc)
  274. {
  275. while (optind < argc)
  276. {
  277. char *host = strdup(argv[optind++]);
  278. g_array_append_val(hosts, host);
  279. }
  280. }
  281. else
  282. {
  283. fprintf(stderr, "No hosts specified\n\n");
  284. usage(argv[0]);
  285. }
  286. }
  287. gtk_init(&argc, &argv);
  288. window = GTK_WIDGET(mssh_window_new());
  289. g_signal_connect(G_OBJECT(window), "destroy",
  290. G_CALLBACK(on_mssh_destroy), NULL);
  291. mssh_window_start_session(MSSH_WINDOW(window), env, hosts, cols);
  292. g_datalist_foreach(commands, mssh_window_add_command, window);
  293. MSSH_WINDOW(window)->commands = commands;
  294. gtk_widget_show_all(window);
  295. gtk_main();
  296. return 0;
  297. }