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