1 #include <nm_core.h>
2 #include <nm_utils.h>
3 #include <nm_string.h>
4 #include <nm_vector.h>
5 #include <nm_ncurses.h>
6 #include <nm_vm_control.h>
7 
8 #include <sys/socket.h>
9 #include <sys/stat.h>
10 #include <sys/wait.h>
11 #include <stdlib.h>
12 #include <limits.h>
13 #include <libgen.h>
14 #include <string.h>
15 #include <errno.h>
16 #include <time.h>
17 
18 enum {
19     NM_BLKSIZE      = 131072, /* 128KiB */
20     NM_SOCK_READLEN = 1024,
21 };
22 
23 static char nemu_path[PATH_MAX];
24 
25 #if defined (NM_OS_LINUX) && defined (NM_WITH_SENDFILE)
26 #include <sys/sendfile.h>
27 #endif
28 
29 #if defined (NM_OS_LINUX) && defined (NM_WITH_SENDFILE)
30 static void nm_copy_file_sendfile(int in_fd, int out_fd);
31 #else
32 static void nm_copy_file_default(int in_fd, int out_fd);
33 #endif
34 
nm_bug(const char * fmt,...)35 void nm_bug(const char *fmt, ...)
36 {
37     va_list args;
38     va_start(args, fmt);
39 
40     nm_curses_deinit();
41 
42     vfprintf(stderr, fmt, args);
43     putc('\n', stderr);
44     va_end(args);
45 
46     nm_exit(NM_ERR);
47 }
48 
nm_alloc(size_t size)49 void *nm_alloc(size_t size)
50 {
51     void *p;
52 
53     if ((p = malloc(size)) == NULL)
54         nm_bug("malloc: %s\n", strerror(errno));
55 
56     return p;
57 }
58 
nm_calloc(size_t nmemb,size_t size)59 void *nm_calloc(size_t nmemb, size_t size)
60 {
61     void *p;
62 
63     if ((p = calloc(nmemb, size)) == NULL)
64         nm_bug("cmalloc: %s\n", strerror(errno));
65 
66     return p;
67 }
68 
nm_realloc(void * p,size_t size)69 void *nm_realloc(void *p, size_t size)
70 {
71     void *p_new;
72 
73     if ((p_new = realloc(p, size)) == NULL)
74         nm_bug("realloc: %s\n", strerror(errno));
75 
76     return p_new;
77 }
78 
nm_map_file(nm_file_map_t * file)79 void nm_map_file(nm_file_map_t *file)
80 {
81     struct stat stat;
82 
83     if ((file->fd = open(file->name->data, O_RDONLY)) == -1) {
84         nm_bug(_("Cannot open file %s:%s"),
85             file->name->data, strerror(errno));
86     }
87 
88     if (fstat(file->fd, &stat) == -1) {
89         close(file->fd);
90         nm_bug(_("Cannot get file info %s:%s"),
91             file->name->data, strerror(errno));
92     }
93 
94     file->size = stat.st_size;
95     file->mp = mmap(0, file->size, PROT_READ, MAP_PRIVATE, file->fd, 0);
96 
97     if (file->mp == MAP_FAILED) {
98         close(file->fd);
99         nm_bug(_("%s: cannot map file %s:%s"),
100             __func__, file->name->data, strerror(errno));
101     }
102 }
103 
nm_unmap_file(const nm_file_map_t * file)104 void nm_unmap_file(const nm_file_map_t *file)
105 {
106     munmap(file->mp, file->size);
107     close(file->fd);
108 }
109 
nm_copy_file(const nm_str_t * src,const nm_str_t * dst)110 void nm_copy_file(const nm_str_t *src, const nm_str_t *dst)
111 {
112     int in_fd, out_fd;
113 
114     if ((in_fd = open(src->data, O_RDONLY)) == -1) {
115         nm_bug("%s: cannot open file %s: %s",
116             __func__, src->data, strerror(errno));
117     }
118 
119     if ((out_fd = open(dst->data, O_WRONLY | O_CREAT | O_EXCL, 0644)) == -1) {
120         close(in_fd);
121         nm_bug("%s: cannot open file %s: %s",
122             __func__, dst->data, strerror(errno));
123     }
124 
125 #if defined (NM_OS_LINUX) && defined (NM_WITH_SENDFILE)
126     nm_copy_file_sendfile(in_fd, out_fd);
127 #else
128     nm_copy_file_default(in_fd, out_fd);
129 #endif
130 
131     close(in_fd);
132     close(out_fd);
133 }
134 
135 #if defined (NM_OS_LINUX) && defined (NM_WITH_SENDFILE)
nm_copy_file_sendfile(int in_fd,int out_fd)136 static void nm_copy_file_sendfile(int in_fd, int out_fd)
137 {
138     off_t offset = 0;
139     struct stat file_info;
140 
141     memset(&file_info, 0, sizeof(file_info));
142 
143     if (fstat(in_fd, &file_info) != 0)
144         nm_bug("%s: cannot get file info %d: %s", __func__, in_fd, strerror(errno));
145 
146     while (offset < file_info.st_size) {
147         int rc;
148 
149         if ((rc = sendfile(out_fd, in_fd, &offset, file_info.st_size)) == -1)
150             nm_bug("%s: cannot copy file: %s", __func__, strerror(errno));
151 
152         if (rc == 0)
153             break;
154     }
155 
156     if (offset != file_info.st_size)
157             nm_bug("%s: incomplete transfer from sendfile", __func__);
158 }
159 #else
nm_copy_file_default(int in_fd,int out_fd)160 static void nm_copy_file_default(int in_fd, int out_fd)
161 {
162     char *buf = nm_alloc(NM_BLKSIZE);
163     ssize_t nread;
164 
165     posix_fadvise(in_fd, 0, 0, POSIX_FADV_SEQUENTIAL);
166 
167     while ((nread = read(in_fd, buf, NM_BLKSIZE)) > 0) {
168         char *bufsp = buf;
169 
170         do {
171             ssize_t nwrite = write(out_fd, bufsp, NM_BLKSIZE);
172 
173             if (nwrite >= 0) {
174                 nread -= nwrite;
175                 bufsp += nwrite;
176             } else if (errno != EINTR) {
177                 nm_bug("%s: copy file failed: %s", __func__, strerror(errno));
178             }
179 
180         } while (nread > 0);
181     }
182 
183     if (nread != 0)
184         nm_bug("%s: copy was not complete.", __func__);
185 
186     free(buf);
187 }
188 #endif
189 
nm_spawn_process(const nm_vect_t * argv,nm_str_t * answer)190 int nm_spawn_process(const nm_vect_t *argv, nm_str_t *answer)
191 {
192     int rc = NM_OK;
193     int fd[2];
194     pid_t child_pid = 0;
195 
196     if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) == -1)
197         nm_bug("%s: error create socketpair: %s", __func__, strerror(errno));
198 
199     switch (child_pid = fork()) {
200     case (-1):  /* error*/
201         nm_bug("%s: fork: %s", __func__, strerror(errno));
202         break;
203 
204     case (0):   /* child */
205         close(fd[0]);
206         dup2(fd[1], STDOUT_FILENO);
207         dup2(fd[1], STDERR_FILENO);
208 
209         execvp(((char *const *) argv->data)[0], (char *const *) argv->data);
210         nm_bug("%s: unreachable reached", __func__);
211         break;
212 
213     default:    /* parent */
214         {
215             int wstatus = 0;
216             pid_t w_rc;
217             char buf[NM_SOCK_READLEN] = {0};
218 
219             close(fd[1]);
220             w_rc = waitpid(child_pid, &wstatus, 0);
221 
222             if ((w_rc == child_pid) && (WEXITSTATUS(wstatus) != 0)) {
223                 nm_str_t err_msg = NM_INIT_STR;
224 
225                 while (read(fd[0], buf, sizeof(buf) - 1) > 0) {
226                     nm_str_add_text(&err_msg, buf);
227                     memset(&buf, 0, sizeof(buf));
228                 }
229                 nm_vmctl_log_last(&err_msg);
230                 nm_debug("exec_error: %s", err_msg.data);
231                 nm_str_free(&err_msg);
232                 rc = NM_ERR;
233             } else if (answer && (w_rc == child_pid) && (WEXITSTATUS(wstatus) == 0)) {
234                 while (read(fd[0], buf, sizeof(buf) - 1) > 0) {
235                     nm_str_add_text(answer, buf);
236                     memset(buf, 0, sizeof(buf));
237                 }
238             }
239 
240             close(fd[0]);
241         }
242     }
243 
244     return rc;
245 }
246 
nm_debug(const char * fmt,...)247 void nm_debug(const char *fmt, ...)
248 {
249     const nm_cfg_t *cfg = nm_cfg_get();
250     if(!cfg->debug)
251         return;
252 
253     va_list args;
254     FILE *fp;
255 
256     if ((fp = fopen(cfg->debug_path.data, "a+")) == NULL)
257         return;
258 
259     va_start(args, fmt);
260     vfprintf(fp, fmt, args);
261     va_end(args);
262 
263     fclose(fp);
264 }
265 
nm_cmd_str(nm_str_t * str,const nm_vect_t * argv)266 void nm_cmd_str(nm_str_t *str, const nm_vect_t *argv)
267 {
268     if (str->len > 0)
269         nm_str_trunc(str, 0);
270 
271     for (size_t m = 0; m < argv->n_memb; m++) {
272         nm_str_append_format(str, "%s ", (char *)nm_vect_at(argv, m));
273     }
274 }
275 
276 /* SMP format: sockets:cores?:threads?
277  * if only one value is specified we assume that
278  * N processors with one core are used */
nm_parse_smp(nm_cpu_t * cpu,const char * src)279 void nm_parse_smp(nm_cpu_t *cpu, const char *src)
280 {
281     nm_str_t buf = NM_INIT_STR;
282     char *column;
283     char *saveptr;
284     size_t ncol = 0;
285 
286     nm_str_format(&buf, "%s", src);
287     saveptr = buf.data;
288 
289     while ((column = strtok_r(saveptr, ":", &saveptr))) {
290         switch (ncol) {
291         case 0: /* sockets */
292             cpu->sockets = nm_str_ttoul(column, 10);
293             break;
294         case 1: /* cores */
295             cpu->cores = nm_str_ttoul(column, 10);
296             break;
297         case 2: /* threads */
298             cpu->threads = nm_str_ttoul(column, 10);
299             break;
300         }
301         ncol++;
302     }
303 
304     if (ncol == 1) {
305         cpu->smp = cpu->sockets;
306         cpu->sockets = 0;
307     } else {
308         cpu->smp = cpu->sockets * cpu->cores * ((cpu->threads) ? cpu->threads : 1);
309     }
310 
311     nm_str_free(&buf);
312 }
313 
314 //@TODO call on SIGINT
nm_exit(int status)315 void nm_exit(int status)
316 {
317     if(nm_db_in_transaction())
318         nm_db_rollback();
319 
320     exit(status);
321 }
322 
nm_mkdir_parent(const nm_str_t * path,mode_t mode)323 int nm_mkdir_parent(const nm_str_t *path, mode_t mode)
324 {
325     int rc = NM_OK;
326     nm_vect_t path_tok = NM_INIT_VECT;
327     nm_str_t buf = NM_INIT_STR;
328 
329     if (path->len > PATH_MAX)
330         nm_bug(_("%s: path \"%s\" too long"), __func__, path->data);
331 
332     nm_str_append_to_vect(path, &path_tok, "/");
333 
334     for (size_t n = 0; n < path_tok.n_memb; n++) {
335         nm_str_append_format(&buf, "/%s", (char *)path_tok.data[n]);
336         rc = mkdir(buf.data, mode);
337         if (rc < 0 && errno != EEXIST) {
338             nm_debug(_("%s: failed to create directory \"%s\" (%s)"),
339                     __func__, buf.data, strerror(errno));
340             fprintf(stderr, _("%s: failed to create directory \"%s\" (%s)"),
341                     __func__, buf.data, strerror(errno));
342             break;
343         } else {
344             rc = NM_OK;
345         }
346     }
347 
348     nm_str_free(&buf);
349     nm_vect_free(&path_tok, NULL);
350     return rc;
351 }
352 
nm_nemu_path()353 const char *nm_nemu_path()
354 {
355     if (readlink("/proc/self/exe", nemu_path, PATH_MAX) < 0)
356         nm_bug(_("%s: failed getting nemu binary path (%s)"),
357             __func__, strerror(errno)
358         );
359     return nemu_path;
360 }
361 
nm_get_time(nm_str_t * res,const char * fmt)362 void nm_get_time(nm_str_t *res, const char *fmt)
363 {
364     struct tm tm;
365     time_t now;
366 
367     if (!res || !fmt) {
368         return;
369     }
370 
371     if (time(&now) == -1) {
372         nm_bug(_("%s: cannot get time"), __func__);
373     }
374 
375     if (localtime_r(&now, &tm) == NULL) {
376         nm_bug(_("%s: cannot transform time"), __func__);
377     }
378 
379     if (res->len) {
380         nm_str_trunc(res, 0);
381     }
382 
383     /* strftime(3) is bad desined, so we write own... */
384     while (*fmt) {
385         char ch = *fmt;
386         char ch_next = *(fmt + 1);
387 
388         if (ch == '%' && ch_next != '\0') {
389             fmt++;
390             switch (ch_next) {
391             case 'Y':
392                 nm_str_append_format(res, "%d", tm.tm_year + 1900);
393                 break;
394             case 'm':
395                 nm_str_append_format(res, "%02d", tm.tm_mon + 1);
396                 break;
397             case 'd':
398                 nm_str_append_format(res, "%02d", tm.tm_mday);
399                 break;
400             case 'H':
401                 nm_str_append_format(res, "%02d", tm.tm_hour);
402                 break;
403             case 'M':
404                 nm_str_append_format(res, "%02d", tm.tm_min);
405                 break;
406             case 'S':
407                 nm_str_append_format(res, "%02d", tm.tm_sec);
408                 break;
409             case '%':
410                 nm_str_add_char_opt(res, '%');
411                 break;
412             default:
413                 nm_bug(_("%s: bad format: %%%c"), __func__, ch_next);
414             }
415         } else {
416             nm_str_add_char_opt(res, ch);
417         }
418 
419         fmt++;
420     }
421 }
422 
nm_gen_rand_str(nm_str_t * res,size_t len)423 void nm_gen_rand_str(nm_str_t *res, size_t len)
424 {
425     const char rnd[] = "0123456789"
426         "abcdefghijklmnopqrstuvwxyz"
427         "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
428     struct timespec ts;
429 
430     if (!res) {
431         return;
432     }
433 
434     if (res->len) {
435         nm_str_trunc(res, 0);
436     }
437 
438     clock_gettime(CLOCK_MONOTONIC, &ts);
439     srand((time_t) ts.tv_nsec);
440 
441     while (len) {
442         nm_str_add_char_opt(res, rnd[rand() % (sizeof(rnd) - 1)]);
443         len--;
444     }
445 }
446 
nm_gen_uid(nm_str_t * res)447 void nm_gen_uid(nm_str_t *res)
448 {
449     const char fmt[] = "%Y-%m-%d-%H-%M-%S";
450     nm_str_t time = NM_INIT_STR;
451     nm_str_t rnd = NM_INIT_STR;
452 
453     if (!res) {
454         return;
455     }
456 
457     if (res->len) {
458         nm_str_trunc(res, 0);
459     }
460 
461     nm_get_time(&time, fmt);
462     nm_gen_rand_str(&rnd, 8);
463 
464     nm_str_format(res, "%s-%s", time.data, rnd.data);
465 
466     nm_str_free(&time);
467     nm_str_free(&rnd);
468 }
469 
470 /* vim:set ts=4 sw=4: */
471