1 // This file is part of BOINC.
2 // http://boinc.berkeley.edu
3 // Copyright (C) 2008 University of California
4 //
5 // BOINC is free software; you can redistribute it and/or modify it
6 // under the terms of the GNU Lesser General Public License
7 // as published by the Free Software Foundation,
8 // either version 3 of the License, or (at your option) any later version.
9 //
10 // BOINC is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 // See the GNU Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public License
16 // along with BOINC. If not, see <http://www.gnu.org/licenses/>.
17
18 // Utility functions for server software (not just scheduler)
19
20 #include "config.h"
21 #include <cstdlib>
22 #include <csignal>
23 #include <cerrno>
24 #include <unistd.h>
25 #include <sys/stat.h>
26 #include <sys/types.h>
27 #include <fcntl.h>
28
29 #include "error_numbers.h"
30 #include "filesys.h"
31 #include "md5_file.h"
32 #include "util.h"
33 #include "str_replace.h"
34
35 #include "sched_config.h"
36 #include "sched_msgs.h"
37 #include "sched_util.h"
38
39 #ifdef _USING_FCGI_
40 #include "boinc_fcgi.h"
41 #endif
42
43 const char* STOP_DAEMONS_FILENAME = "stop_daemons";
44 // NOTE: this must be same as in the "start" script
45 const char* STOP_SCHED_FILENAME = "stop_sched";
46 // NOTE: this must be same as in the "start" script
47 const int STOP_SIGNAL = SIGHUP;
48 // NOTE: this must be same as in the "start" script
49
write_pid_file(const char * filename)50 void write_pid_file(const char* filename) {
51 #ifndef _USING_FCGI_
52 FILE* fpid = fopen(filename, "w");
53 #else
54 FCGI_FILE* fpid = FCGI::fopen(filename,"w");
55 #endif
56
57 if (!fpid) {
58 log_messages.printf(MSG_CRITICAL, "Couldn't write pid file\n");
59 return;
60 }
61 fprintf(fpid, "%d\n", (int)getpid());
62 fclose(fpid);
63 }
64
65 // caught_sig_int will be set to true if STOP_SIGNAL (normally SIGHUP)
66 // is caught.
67 bool caught_stop_signal = false;
stop_signal_handler(int)68 static void stop_signal_handler(int) {
69 fprintf(stderr, "GOT STOP SIGNAL\n");
70 caught_stop_signal = true;
71 }
72
install_stop_signal_handler()73 void install_stop_signal_handler() {
74 signal(STOP_SIGNAL, stop_signal_handler);
75 // handler is now default again so hitting ^C again will kill the program.
76 }
77
check_stop_daemons()78 void check_stop_daemons() {
79 if (caught_stop_signal) {
80 log_messages.printf(MSG_NORMAL, "Quitting due to SIGHUP\n");
81 exit(0);
82 }
83 const char *stop_file = config.project_path(STOP_DAEMONS_FILENAME);
84 if (boinc_file_exists(stop_file)) {
85 log_messages.printf(MSG_NORMAL,
86 "Quitting because trigger file '%s' is present\n",
87 stop_file
88 );
89 exit(0);
90 }
91 }
92
93 // sleep for n seconds, but check every second for trigger file
94 //
daemon_sleep(int nsecs)95 void daemon_sleep(int nsecs) {
96 for (int i=0; i<nsecs; i++) {
97 check_stop_daemons();
98 sleep(1);
99 }
100 }
101
check_stop_sched()102 bool check_stop_sched() {
103 return boinc_file_exists(config.project_path(STOP_SCHED_FILENAME));
104 }
105
106 // try to open a file.
107 // On failure:
108 // return ERR_FOPEN if the dir is there but not file
109 // (this is generally a nonrecoverable failure)
110 // return ERR_OPENDIR if dir is not there.
111 // (this is generally a recoverable error,
112 // like NFS mount failure, that may go away later)
113 //
114 #ifndef _USING_FCGI_
try_fopen(const char * path,FILE * & f,const char * mode)115 int try_fopen(const char* path, FILE*& f, const char* mode) {
116 #else
117 int try_fopen(const char* path, FCGI_FILE*& f, const char *mode) {
118 #endif
119 const char* p;
120 DIR* d;
121 char dirpath[MAXPATHLEN];
122
123 #ifndef _USING_FCGI_
124 f = fopen(path, mode);
125 #else
126 f = FCGI::fopen(path, mode);
127 #endif
128
129 if (!f) {
130 memset(dirpath, '\0', sizeof(dirpath));
131 p = strrchr(path, '/');
132 if (p) {
133 strncpy(dirpath, path, (int)(p-path));
134 } else {
135 strcpy(dirpath, ".");
136 }
137 if ((d = opendir(dirpath)) == NULL) {
138 return ERR_OPENDIR;
139 } else {
140 closedir(d);
141 return ERR_FOPEN;
142 }
143 }
144 return 0;
145 }
146
147 int get_log_path(char* p, const char* filename) {
148 char host[256];
149 const char *dir;
150
151 gethostname(host, 256);
152 char* q = strchr(host, '.');
153 if (q) *q=0;
154 dir = config.project_path("log_%s", host);
155 sprintf(p, "%s/%s", dir, filename);
156 mode_t old_mask = umask(0);
157 // make log_x directory sticky and group-rwx
158 // so that whatever apache puts there will be owned by us
159 int retval = mkdir(dir, 01770);
160 umask(old_mask);
161 if (retval && errno != EEXIST) return ERR_MKDIR;
162
163 return 0;
164 }
165
166 static void filename_hash(const char* filename, int fanout, char* dir) {
167 std::string s = md5_string((const unsigned char*)filename, strlen(filename));
168 int x = strtol(s.substr(1, 7).c_str(), 0, 16);
169 sprintf(dir, "%x", x % fanout);
170 }
171
172 // given a filename, compute its path in a directory hierarchy
173 // If create is true, create the directory if needed
174 //
175 int dir_hier_path(
176 const char* filename, const char* root, int fanout,
177 char* path, bool create
178 ) {
179 char dir[256], dirpath[MAXPATHLEN];
180 int retval;
181
182 if (fanout==0) {
183 snprintf(path, MAXPATHLEN, "%s/%s", root, filename);
184 return 0;
185 }
186
187 filename_hash(filename, fanout, dir);
188
189 snprintf(dirpath, MAXPATHLEN, "%s/%s", root, dir);
190 if (create) {
191 retval = boinc_mkdir(dirpath);
192 if (retval && (errno != EEXIST)) {
193 fprintf(stderr, "boinc_mkdir(%s): %s: errno %d\n",
194 dirpath, boincerror(retval), errno
195 );
196 return ERR_MKDIR;
197 }
198 }
199 snprintf(path, MAXPATHLEN, "%s/%s", dirpath, filename);
200 return 0;
201 }
202
203 // same, but the output is a URL (used by tools/backend_lib.C)
204 //
205 int dir_hier_url(
206 const char* filename, const char* root, int fanout,
207 char* result
208 ) {
209 char dir[256];
210
211 if (fanout==0) {
212 sprintf(result, "%s/%s", root, filename);
213 return 0;
214 }
215
216 filename_hash(filename, fanout, dir);
217 sprintf(result, "%s/%s/%s", root, dir, filename);
218 return 0;
219 }
220
221 // Request lock on the given file with given fd. Returns:
222 // 0 if we get lock
223 // PID (>0) if another process has lock
224 // -1 if error
225 //
226 int mylockf(int fd) {
227 struct flock fl;
228 fl.l_type=F_WRLCK;
229 fl.l_whence=SEEK_SET;
230 fl.l_start=0;
231 fl.l_len=0;
232 if (-1 != fcntl(fd, F_SETLK, &fl)) return 0;
233
234 // if lock failed, find out why
235 errno=0;
236 // coverity[check_return]
237 fcntl(fd, F_GETLK, &fl);
238 if (fl.l_pid>0) return fl.l_pid;
239 return -1;
240 }
241
242 bool is_arg(const char* x, const char* y) {
243 char buf[256];
244 strcpy(buf, "--");
245 safe_strcat(buf, y);
246 if (!strcmp(buf, x)) return true;
247 if (!strcmp(buf+1, x)) return true;
248 return false;
249 }
250
251 // the following is used:
252 // - to enforce limits on in-progress jobs for GPUs and CPUs
253 // (see handle_request.cpp)
254 // - to determine what resources the project has apps for (sched_shmem.cpp)
255 //
256 int plan_class_to_proc_type(const char* plan_class) {
257 if (strstr(plan_class, "cuda")) {
258 return PROC_TYPE_NVIDIA_GPU;
259 }
260 if (strstr(plan_class, "nvidia")) {
261 return PROC_TYPE_NVIDIA_GPU;
262 }
263 if (strstr(plan_class, "ati")) {
264 return PROC_TYPE_AMD_GPU;
265 }
266 if (strstr(plan_class, "intel_gpu")) {
267 return PROC_TYPE_INTEL_GPU;
268 }
269 if (strstr(plan_class, "miner_asic")) {
270 return PROC_TYPE_MINER_ASIC;
271 }
272 return PROC_TYPE_CPU;
273 }
274
275 #ifdef GCL_SIMULATOR
276
277 void simulator_signal_handler(int signum) {
278 FILE *fsim;
279 char currenttime[64];
280 fsim = fopen(config.project_path("simulator/sim_time.txt"),"r");
281 if(fsim){
282 fscanf(fsim,"%s", currenttime);
283 simtime = atof(currenttime);
284 fclose(fsim);
285 }
286 log_messages.printf(MSG_NORMAL,
287 "Invoked by the simulator at time %.0f... \n", simtime
288 );
289 }
290
291 int itime() {
292 return (int) simtime;
293 }
294
295 void continue_simulation(const char *daemonname){
296 char daemonfilelok[64];
297 char daemonfile[64];
298 sprintf(daemonfile, strcat((char*)config.project_path("simulator/"),"sim_%s.txt"),daemonname);
299 sprintf(daemonfilelok, strcat((char*)config.project_path("simulator/"),"sim_%s.lok"),daemonname);
300 FILE *fsimlok = fopen(daemonfilelok, "w");
301 if (fsimlok){
302 fclose(fsimlok);
303 FILE *fsim = fopen(daemonfile, "w");
304 if (fsim) {
305 fclose(fsim);
306 }
307 }
308 remove(daemonfilelok);
309 }
310
311 #endif
312
313 const char *BOINC_RCSID_affa6ef1e4 = "$Id$";
314