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