2
Copyright (C) 2003 by Sean David Fleming
6
This program is free software; you can redistribute it and/or
7
modify it under the terms of the GNU General Public License
8
as published by the Free Software Foundation; either version 2
9
of the License, or (at your option) any later version.
11
This program is distributed in the hope that it will be useful,
12
but WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
GNU General Public License for more details.
16
You should have received a copy of the GNU General Public License
17
along with this program; if not, write to the Free Software
18
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20
The GNU GPL can also be found at http://www.gnu.org
34
/* top level data structure */
35
extern struct sysenv_pak sysenv;
37
/* NB: this host stuff could equally well be the localhost, */
38
/* instead of some remote host ... in which case the system() */
39
/* call could be used instead of piping commands via the input */
40
/* and output file descriptors */
42
/* NB: remote stuff, where we issue commands - assumes unix */
43
/* style host ie echo, date, cd ... etc are all assumed to work */
49
gint type; /* unix only at the moment */
55
/* if qsub & mpirun services available - permits alternate invocation */
66
/**************************/
67
/* line reading primitive */
68
/**************************/
69
int readstr(int fd, char *a, int n)
75
if (read(fd, a+i, 1) != 1)
87
/**********************/
88
/* free a service pak */
89
/**********************/
90
void host_service_free(gpointer data)
92
struct service_pak *service = data;
94
g_free(service->fullpath);
98
/**********************************/
99
/* allocate a new host connection */
100
/**********************************/
101
gpointer host_new(gchar *name)
103
struct host_pak *host;
105
host = g_malloc(sizeof(struct host_pak));
107
host->name = g_strdup(name);
109
host->connected = FALSE;
112
host->services = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, host_service_free);
117
/****************************************/
118
/* disconnect and free a host structure */
119
/****************************************/
120
void host_free(gpointer data)
122
struct host_pak *host = data;
127
host_disconnect(host);
131
g_hash_table_destroy(host->services);
136
/*************************/
137
/* free all host entries */
138
/*************************/
139
void host_free_all(void)
143
list = sysenv.host_list;
146
host_free(list->data);
147
list = g_slist_next(list);
149
g_slist_free(sysenv.host_list);
152
/*************************************************/
153
/* perform a foreach on the host's program table */
154
/*************************************************/
155
void host_service_foreach(gpointer data, gpointer func, gpointer arg)
157
struct host_pak *host = data;
159
g_assert(host != NULL);
161
g_hash_table_foreach(host->services, func, arg);
164
/*******************************/
165
/* structure access primitives */
166
/*******************************/
167
const gchar *host_name(gpointer data)
169
struct host_pak *host = data;
171
g_assert(host != NULL);
176
const gchar *host_cwd(gpointer data)
178
struct host_pak *host = data;
180
g_assert(host != NULL);
185
gpointer host_service_get(gpointer data, const gchar *name)
187
struct host_pak *host = data;
189
g_assert(host != NULL);
191
return(g_hash_table_lookup(host->services, name));
194
gchar *host_service_fullpath(gpointer data)
196
struct service_pak *service = data;
198
return(service->fullpath);
201
gint host_service_flags(gpointer data)
203
struct service_pak *service = data;
205
return(service->flags);
208
/*****************************************/
209
/* cycle through service execution types */
210
/*****************************************/
211
void host_service_cycle(gpointer data, const gchar *name)
213
struct host_pak *host = data;
214
struct service_pak *service;
216
service = g_hash_table_lookup(host->services, name);
220
/* don't cycle support services (eg qsub/mpi) */
221
if (service->flags == SERVICE_SECONDARY)
224
/* cycle the service type */
226
if (service->flags >= SERVICE_PRIMARY)
227
service->flags = SERVICE_BACKGROUND;
230
/**********************************************************************************/
231
/* check if a service name (and current flags) is available for the supplied host */
232
/**********************************************************************************/
233
gint host_service_available(gpointer data)
235
struct service_pak *service = data, *mpi, *qsub;
236
struct host_pak *host;
238
g_assert(service != NULL);
239
if (!service->fullpath)
242
host = service->host;
243
mpi = g_hash_table_lookup(host->services, "mpirun");
244
qsub = g_hash_table_lookup(host->services, "qsub");
246
switch (service->flags)
256
case SERVICE_QSUB_MPI:
257
if (!mpi->fullpath || !qsub->fullpath)
265
/***************************************************/
266
/* primitive for populating the host program table */
267
/***************************************************/
268
/* NB: like all, assumes unix host */
269
void host_service_find(struct host_pak *host, const gchar *name)
273
struct service_pak *service;
275
g_assert(host != NULL);
276
g_assert(name != NULL);
278
/* sepcial case services */
279
if (g_strrstr(name, "mpi") || g_strrstr(name, "qsub"))
280
flags = SERVICE_SECONDARY;
282
flags = SERVICE_BACKGROUND;
284
/* attempt to locate executable */
285
text = g_strdup_printf("which %s\n", name);
286
reply = host_talk(host, text);
289
if (g_strrstr(reply, "not found"))
295
/* add the service */
296
service = g_malloc(sizeof(struct service_pak));
297
service->host = host;
298
service->flags = flags;
299
service->fullpath = reply;
301
g_hash_table_insert(host->services, g_strdup(name), service);
304
/**********************************************************/
305
/* setup session working directory for job file transfers */
306
/**********************************************************/
307
/* NB: assumes unix style remote host */
308
/* TODO - check for host type etc etc - which can be reported via the job/host gui */
309
void host_init(struct host_pak *host)
311
gchar *date, *text, *reply;
313
date = host_talk(host, "date\n");
315
/* force existence of gdis ssh session top level directory */
316
reply = host_talk(host, "md gdis_ssh\n");
318
reply = host_talk(host, "cd gdis_ssh\n");
321
/* create a working directory using current date */
322
text = g_strdup_printf("md \"%s\"\n", date);
323
reply = host_talk(host, text);
326
text = g_strdup_printf("cd \"%s\"\n", date);
327
reply = host_talk(host, text);
333
host->cwd = host_talk(host, "pwd\n");
336
/* create a (dummy) control file */
337
/* TODO (maybe) ... could be used to save/restore info about submitted jobs */
338
reply = host_talk(host, "echo \"1\" > control.txt\n");
343
/* initialize available host services */
344
host_service_find(host, "gulp");
346
/* secondary (enabling) services */
347
host_service_find(host, "qsub");
348
host_service_find(host, "mpirun");
350
/* register the initialized host */
351
sysenv.host_list = g_slist_prepend(sysenv.host_list, host);
354
/**********************************/
355
/* activate a host ssh connection */
356
/**********************************/
357
/* NB: assumes unix style remote host */
358
gint host_connect(gpointer data)
364
struct host_pak *host = data;
366
g_assert(host != NULL);
369
perror("Fatal: no host name.");
372
if (pipe(fdto) == -1 || pipe(fdfrom) == -1)
374
perror("Fatal: failed to create pipes.");
381
perror("Fatal: failed to fork.");
386
/* setup child io streams */
387
dup2(fdto[0], fileno(stdin));
388
dup2(fdfrom[1], fileno(stdout));
389
/* close unwanted streams */
393
/* FIXME - can we get rid of the stdin not a terminal complaint? */
395
execlp("ssh", "ssh", "-e", "none", "-T", "-q", host->name, (char *) 0);
398
execlp("ssh", "ssh", "-T", "-q", host->name, (char *) 0);
404
/* close unwanted streams */
409
write(fdto[1], "echo \"ssh:connect\"\n", 19);
411
/* read/write to child (ssh) via file desc. */
412
/* read -> fdfrom[0] */
413
/* write -> fdto[1] */
416
n = readstr(fdfrom[0], cmd, sizeof(cmd));
420
/* if we get here ... we've established a connection */
424
if (strstr(cmd, "ssh:connect"))
426
printf("Connection established.\n");
428
host->input = fdto[1];
429
host->output = fdfrom[0];
430
host->connected = TRUE;
432
/* set up session working directory */
440
host->connected = FALSE;
449
/************************************/
450
/* deactivate a host ssh connection */
451
/************************************/
452
void host_disconnect(gpointer data)
454
struct host_pak *host = data;
456
printf("Closing connection to: %s\n", host->name);
458
host->connected = FALSE;
459
write(host->input, "exit\n", 5);
462
/************************************************************/
463
/* send a message to remote host shell, and return response */
464
/************************************************************/
465
/* NB: assumes unix style remote host */
466
gchar *host_talk(gpointer data, const gchar *message)
471
struct host_pak *host = data;
473
g_assert(host != NULL);
475
if (!host->connected)
478
write(host->input, message, strlen(message));
479
write(host->input, "echo \"host:done\"\n", 17);
481
response = g_string_new(NULL);
483
/* TODO - implement some form of timeout response check? */
484
/* if error, either we didnt connect, or host is not unix */
485
/* and didnt understand the echo command */
488
n = readstr(host->output, cmd, sizeof(cmd));
492
if (strstr(cmd, "host:done"))
495
g_string_sprintfa(response, "%s", cmd);
499
return(g_string_free(response, FALSE));
502
/*********************************************/
503
/* ensure input string is safe to print/echo */
504
/*********************************************/
505
/* NB: unix shell specific */
506
gchar *host_safe_text(const gchar *input)
514
output = g_string_new(NULL);
516
for (i=0 ; i<strlen(input) ; i++)
520
/* escape special chars */
523
g_string_sprintfa(output, "\\%c", input[i]);
527
/* disallow control chars */
528
if (g_ascii_iscntrl(input[i]))
530
g_string_sprintfa(output, "%c", input[i]);
534
return(g_string_free(output, FALSE));
537
/*****************************************/
538
/* write a locally stored file to a host */
539
/*****************************************/
540
/* FIXME - this works ... but is ugly and slow ... the experimental version is better */
541
/* (ie using ascii-xfr) but doesnt terminate properly */
542
gint host_file_write(gpointer data, gchar *local, gchar *remote)
544
gchar *text, *tmp, *reply;
546
struct host_pak *host = data;
548
g_assert(host != NULL);
550
scan = scan_new(local);
553
printf("Error: missing local file: %s\n", local);
557
/* create the remote file */
558
text = g_strdup_printf("echo -n \"\" > \"%s\"\n", remote);
560
reply = host_talk(host, text);
565
/* concat line by line */
566
/* FIXME - is there a better way to do this? */
569
/* escape special characters ... remove control codes */
570
tmp = host_safe_text(scan_get_line(scan));
571
if (scan_complete(scan))
574
text = g_strdup_printf("echo \"%s\" >> \"%s\"\n", tmp, remote);
577
reply = host_talk(host, text);
585
/*****************************************/
586
/* write a locally stored file to a host */
587
/*****************************************/
588
/* PROBLEM is - doesnt seem to terminate properly */
589
gint host_file_write_experimental(gpointer data, gchar *local, gchar *remote)
592
struct host_pak *host = data;
595
if (!host->connected)
598
scan = scan_new(local);
602
text = g_strdup_printf("ascii-xfr -v -r %s\n", remote);
603
write(host->input, text, strlen(text));
608
text = scan_get_line(scan);
609
if (scan_complete(scan))
611
/* FIXME - does not seem to be terminating the transfer ... */
612
write(host->input, "", 1);
613
write(host->input, "", 1);
616
write(host->input, text, strlen(text));
624
/************************************************/
625
/* read a file on a remote host to a local file */
626
/************************************************/
627
gint host_file_read(gpointer data, gchar *local, gchar *remote)
629
gchar *text, cmd[10];
630
struct host_pak *host = data;
633
if (!host->connected)
636
text = g_strdup_printf("ascii-xfr -v -s -e %s\n", remote);
638
write(host->input, text, strlen(text));
640
fp = fopen(local, "w");
644
read(host->output, cmd, 1);
646
fprintf(fp, "%c", cmd[0]);
648
if (cmd[0] == 26 || cmd[0] == '')
657
/****************************************************/
658
/* simple test of the host communication primitives */
659
/****************************************************/
660
/* NB: assumes unix style remote host */
666
host = host_new("sean@onyx.ivec.org");
668
if (host_connect(host))
669
printf("host_connect(): success!\n");
672
printf("host_connect(): failed!\n");
679
struct host_pak *h = host;
681
write(h->input, "cat > gok.txt\n", 14);
682
write(h->input, "abcdef\n", 7);
685
/* CURRENT - how do we terminate the redirect ... ie simulate a control-D or control-C */
686
/* appears there is no portable way for doing it ... can send stuff to /dev/tty in linux */
687
/* none of these work ... */
689
write(h->input, (char *) '~', 1);
690
write(h->input, (char *) 'd', 1);
691
write(h->input, (char *) "^C", 2);
692
write(h->input, (char *) "^D", 2);
693
write(h->input, (char *) "", 1);
694
write(h->input, (char *) "", 1);
695
write(h->input, (char *) 27, 1);
696
write(h->input, (char *) "", 1);
697
write(h->input, (char *) 16, 1);
698
write(h->input, (char *) "", 1);
704
printf("sending: [date]\n");
705
reply = host_talk(host, "date\n");
706
printf("response: [%s]\n", reply);
711
printf("sending: [which gulp]\n");
712
reply = host_talk(host, "which gulp\n");
713
printf("response: [%s]\n", reply);
716
/* CURRENT - using ascii-xfr to accomplish these */
717
/* FIXME - read then write works ... but reversing order causes a hang ... why??? */
718
/* seems that after a write has been done ... things are messed up */
721
host_file_read(host, "dummy.txt", "/home/sean/.cshrc");
722
host_file_write(host, "aloh4-.car", "aloh4-copy.car");
723
host_file_write(host, "aloh4-.car", "aloh4-copy2.car");
728
host_disconnect(host);