mssh.c 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  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 = index(sep + 1, ' ')) == NULL)
  166. {
  167. printf("Line %d: Command Alias '%s' specifies no command\n", lineno,
  168. command);
  169. exit(EXIT_FAILURE);
  170. }
  171. while ( *commandline == ' ' ) { commandline++; }
  172. sep = commandline;
  173. while ( ( sep = index(sep, '\\') ) != NULL ) {
  174. if ( *(sep+1) == 'n' ) {
  175. *sep = ' ';
  176. *(sep+1) = '\n';
  177. }
  178. sep++;
  179. }
  180. g_datalist_set_data(commands, command, commandline);
  181. }
  182. return commands;
  183. }
  184. int main(int argc, char* argv[], char* env[])
  185. {
  186. GtkWidget* window;
  187. int c, option_index = 0;
  188. char *home, *conffile;
  189. long cols = 0;
  190. GData **aliases = NULL;
  191. GData **commands = NULL;
  192. GArray *hosts = NULL;
  193. static struct option long_options[] =
  194. {
  195. {"alias", required_argument, 0, 'a'},
  196. {"columns", required_argument, 0, 'c'},
  197. {"help", no_argument, 0, 'h'},
  198. {"version", no_argument, 0, 'V'},
  199. {0, 0, 0, 0}
  200. };
  201. if((home = getenv("HOME")) != NULL)
  202. {
  203. int len = strlen(home) + strlen(CONFFILE) + 2;
  204. conffile = malloc(len);
  205. snprintf(conffile, len, "%s/%s", home, CONFFILE);
  206. aliases = parse_aliases(conffile);
  207. commands = parse_commands(conffile);
  208. free(conffile);
  209. }
  210. else
  211. {
  212. fprintf(stderr,
  213. "Warning: $HOME not set, not reading config file\n");
  214. }
  215. for(;;)
  216. {
  217. c = getopt_long(argc, argv, "a:c:hV", long_options, &option_index);
  218. if(c == -1)
  219. break;
  220. switch(c)
  221. {
  222. case 'a':
  223. if(aliases && (hosts = g_datalist_get_data(aliases,
  224. optarg)) == NULL)
  225. {
  226. fprintf(stderr, "Alias '%s' not found\n\n", optarg);
  227. usage(argv[0]);
  228. }
  229. break;
  230. case 'c':
  231. errno = 0;
  232. cols = strtol(optarg, NULL, 10);
  233. if(cols <= 0 || errno != 0)
  234. {
  235. fprintf(stderr, "Invalid number of columns '%s'\n\n",
  236. optarg);
  237. usage(argv[0]);
  238. }
  239. break;
  240. case 'h':
  241. usage(argv[0]);
  242. break;
  243. case 'V':
  244. printf("%s\n\n", PKGINFO);
  245. printf("%s\n\n", COPYRIGHT);
  246. printf("Redistribution and use in source and binary forms, with or without\n");
  247. printf("modification, are permitted provided that the following conditions are met:\n");
  248. printf("\n");
  249. printf(" 1. Redistributions of source code must retain the copyright notice,\n");
  250. printf(" this list of conditions and the following disclaimer.\n");
  251. printf(" 2. Redistributions in binary form must reproduce the copyright notice,\n");
  252. printf(" this list of conditions and the following disclaimer in the\n");
  253. printf(" documentation and/or other materials provided with the distribution.\n");
  254. printf(" 3. The name of the author may not be used to endorse or promote\n");
  255. printf(" products derived from this software without specific prior written\n");
  256. printf(" permission.\n");
  257. printf("\n");
  258. printf("THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n");
  259. printf("IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n");
  260. printf("OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN\n");
  261. printf("NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n");
  262. printf("SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\n");
  263. printf("TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n");
  264. printf("PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n");
  265. printf("LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n");
  266. printf("NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n");
  267. printf("SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n");
  268. exit(EXIT_SUCCESS);
  269. break;
  270. case '?':
  271. printf("\n");
  272. usage(argv[0]);
  273. exit(EXIT_FAILURE);
  274. break;
  275. default:
  276. abort();
  277. }
  278. }
  279. if(hosts == NULL)
  280. {
  281. hosts = g_array_new(FALSE, TRUE, sizeof(char*));
  282. if (optind < argc)
  283. {
  284. while (optind < argc)
  285. {
  286. char *host = strdup(argv[optind++]);
  287. g_array_append_val(hosts, host);
  288. }
  289. }
  290. else
  291. {
  292. fprintf(stderr, "No hosts specified\n\n");
  293. usage(argv[0]);
  294. }
  295. }
  296. gtk_init(&argc, &argv);
  297. window = GTK_WIDGET(mssh_window_new());
  298. g_signal_connect(G_OBJECT(window), "destroy",
  299. G_CALLBACK(on_mssh_destroy), NULL);
  300. MSSH_WINDOW(window)->commands = commands;
  301. mssh_window_start_session(MSSH_WINDOW(window), env, hosts, cols);
  302. g_datalist_foreach(commands, mssh_window_add_command, window);
  303. gtk_widget_show_all(window);
  304. gtk_main();
  305. return 0;
  306. }