-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmjsWriteSshScript.m
167 lines (142 loc) · 5.15 KB
/
mjsWriteSshScript.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
function sshScriptFile = mjsWriteSshScript(jobScriptFile, varargin)
% Write a shell script that will invoke a given jobScriptFile via SSH.
%
% This saves us from having to write lots of SSH syntax by hand.
%
% sshScriptFile = mjsWriteSshScript(jobScriptFile) generates a shell
% script that will cause the given jobScriptFile be executed remotely,
% via SSH.
%
% mjsWriteSshScript( ... 'sshScriptFile', sshScriptFile) specify the name
% of the new SSH script that should be generated. The default is chosen
% based on the jobScriptFile.
%
% mjsWriteSshScript( ... 'host', host) specify the address or hostname of
% the remote host to access via SSH.
%
% mjsWriteSshScript( ... 'port', port) specify the port to connect to on
% the remote host.
%
% mjsWriteSshScript( ... 'user', user) specify the usename to use when
% connecting to the remote host.
%
% mjsWriteSshScript( ... 'identity', identity) specify the path to an
% identity file (often .pem) to use for authenticating with the remote
% host.
%
% mjsWriteSshScript( ... 'knownHostsFile', knownHostsFile) specify the path
% to the ssh "known_hosts" file, where the ssh key of the given host can be
% automatically accepted. The default is '~/.ssh/known_hosts'.
%
% sshScriptFile = mjsWriteSshScript(jobScriptFile, varargin)
%
% 2016-2017 Brainard Lab, University of Pennsylvania
arguments = mjsIncludeEnvironmentProfile(varargin{:});
parser = inputParser();
parser.KeepUnmatched = true;
parser.StructExpand = true;
parser.addRequired('jobScriptFile', @ischar);
parser.addParameter('sshScriptFile', '', @ischar);
parser.addParameter('sshScriptFid', [], @isnumeric);
parser.addParameter('host', 'localhost', @ischar);
parser.addParameter('port', [], @isnumeric);
parser.addParameter('user', '', @ischar);
parser.addParameter('identity', '', @ischar);
parser.addParameter('knownHostsFile', '', @ischar);
parser.parse(jobScriptFile, arguments);
jobScriptFile = parser.Results.jobScriptFile;
sshScriptFile = parser.Results.sshScriptFile;
sshScriptFid = parser.Results.sshScriptFid;
host = parser.Results.host;
port = parser.Results.port;
user = parser.Results.user;
identity = parser.Results.identity;
knownHostsFile = parser.Results.knownHostsFile;
% default ssh script name based on job script name
[jobScriptPath, jobScriptBase] = fileparts(jobScriptFile);
if isempty(sshScriptFile)
sshScriptFile = fullfile(jobScriptPath, [jobScriptBase '-ssh.sh']);
end
% default SSH user is current Matlab user
if isempty(user)
[~, user] = system('whoami');
end
%% Read job script into memory so we can embed it in the ssh script.
fid = fopen(jobScriptFile, 'r');
if -1 == fid
error('mjsWriteSshScript:fopen', ...
'Could not open file <%s> for reading.', jobScriptFile);
end
jobScriptText = fread(fid, '*char')';
fclose(fid);
% excape literal single quotes '
% allows us to enclose the whole job script in strong quotes '...'
% Matlab makes this look bad because it takes '''' to say '
jobScriptText = regexprep(jobScriptText, '''', '''\\''''');
%% Create the ssh script file.
scriptDir = fileparts(sshScriptFile);
if ~isempty(scriptDir) && 7 ~= exist(scriptDir, 'dir')
mkdir(scriptDir);
end
if isempty(sshScriptFid)
fid = fopen(sshScriptFile, 'w');
else
% allow writing to an already-open file
fid = sshScriptFid;
end
if -1 == fid
error('mjsWriteSshScript:fopen', ...
'Could not open file <%s> for writing.', sshScriptFile);
end
try
if isempty(sshScriptFid)
% don't need shebang when writing into an existing file
fprintf(fid, '#!/bin/sh\n');
end
fprintf(fid, '## Begin script generated by mjsWriteSshScript.m\n');
%% Embed the job command here in this script.
fprintf(fid, '\n');
fprintf(fid, '# embed the whole job script in strong quotes ''...''\n');
fprintf(fid, 'COMMAND=''%s''\n', jobScriptText);
%% Try to automatically accept the remote ssh key.
% avoids prompting the user to accept the key
if ~isempty(knownHostsFile)
fprintf(fid, '\n');
fprintf(fid, '# auto-accept remote ssh key to avoid user prompt\n');
fprintf(fid, 'ssh-keyscan -H "%s" >> "%s"\n', host, knownHostsFile);
end
%% Try to invoke the command on the remote host.
if isempty(port)
portOption = '';
else
portOption = sprintf('-p %d', port);
end
if isempty(identity)
idOption = '';
else
idOption = sprintf('-i "%s"', identity);
end
fprintf(fid, '\n');
fprintf(fid, '# invoke ssh with the whole job command\n');
fprintf(fid, 'echo "Running command <$COMMAND> at host <%s> with identity <%s>"\n', host, idOption);
fprintf(fid, 'ssh %s %s \\\n', portOption, idOption);
fprintf(fid, ' "%s@%s" \\\n', user, host);
fprintf(fid, ' "$COMMAND"\n');
fprintf(fid, '\n');
fprintf(fid, 'echo "Done."\n');
fprintf(fid, 'date\n');
fprintf(fid, '\n');
fprintf(fid, '## End script generated by mjsWriteSshScript.m\n');
fprintf(fid, '\n');
if isempty(sshScriptFid)
fclose(fid);
end
catch err
if isempty(sshScriptFid)
fclose(fid);
end
rethrow(err);
end
if isempty(sshScriptFid)
system(['chmod +x ' sshScriptFile]);
end