29 *
30 * Deal with the parallel processing
31 */
32
33 /*
34 * Included files
35 */
36 #include <errno.h> /* errno */
37 #include <fcntl.h>
38 #include <mk/defs.h>
39 #include <mksh/dosys.h> /* redirect_io() */
40 #include <mksh/macro.h> /* expand_value() */
41 #include <mksh/misc.h> /* getmem() */
42 #include <sys/signal.h>
43 #include <sys/stat.h>
44 #include <sys/types.h>
45 #include <sys/utsname.h>
46 #include <sys/wait.h>
47 #include <unistd.h>
48 #include <netdb.h>
49
50
51
52 /*
53 * Defined macros
54 */
55 #define MAXRULES 100
56
57 /*
58 * This const should be in avo_dms/include/AvoDmakeCommand.h
59 */
60 const int local_host_mask = 0x20;
61
62
63 /*
64 * typedefs & structs
65 */
66
67
68 /*
114 int cmd_options = 0;
115 char *commands[MAXRULES + 5];
116 char *cp;
117 Name dmake_name;
118 Name dmake_value;
119 int ignore;
120 Name make_machines_name;
121 char **p;
122 Property prop;
123 Doname result = build_ok;
124 Cmd_line rule;
125 Boolean silent_flag;
126 Name target = line->body.line.target;
127 Boolean wrote_state_file = false;
128
129 if ((pmake_max_jobs == 0) &&
130 (dmake_mode_type == parallel_mode)) {
131 if (local_host[0] == '\0') {
132 (void) gethostname(local_host, MAXNAMELEN);
133 }
134 MBSTOWCS(wcs_buffer, NOCATGETS("DMAKE_MAX_JOBS"));
135 dmake_name = GETNAME(wcs_buffer, FIND_LENGTH);
136 if (((prop = get_prop(dmake_name->prop, macro_prop)) != NULL) &&
137 ((dmake_value = prop->body.macro.value) != NULL)) {
138 pmake_max_jobs = atoi(dmake_value->string_mb);
139 if (pmake_max_jobs <= 0) {
140 warning(catgets(catd, 1, 308, "DMAKE_MAX_JOBS cannot be less than or equal to zero."));
141 warning(catgets(catd, 1, 309, "setting DMAKE_MAX_JOBS to %d."), PMAKE_DEF_MAX_JOBS);
142 pmake_max_jobs = PMAKE_DEF_MAX_JOBS;
143 }
144 } else {
145 /*
146 * For backwards compatibility w/ PMake 1.x, when
147 * DMake 2.x is being run in parallel mode, DMake
148 * should parse the PMake startup file
149 * $(HOME)/.make.machines to get the pmake_max_jobs.
150 */
151 MBSTOWCS(wcs_buffer, NOCATGETS("PMAKE_MACHINESFILE"));
152 dmake_name = GETNAME(wcs_buffer, FIND_LENGTH);
153 if (((prop = get_prop(dmake_name->prop, macro_prop)) != NULL) &&
154 ((dmake_value = prop->body.macro.value) != NULL)) {
155 make_machines_name = dmake_value;
156 } else {
157 make_machines_name = NULL;
158 }
159 if ((pmake_max_jobs = read_make_machines(make_machines_name)) <= 0) {
160 pmake_max_jobs = PMAKE_DEF_MAX_JOBS;
161 }
162 }
163 }
164
165 if ((dmake_mode_type == serial_mode) ||
166 ((dmake_mode_type == parallel_mode) && (waitflg))) {
167 return (execute_serial(line));
168 }
169
170 {
171 p = commands;
305 * ftok(), shmget(), shmat(), shmdt(), shmctl()
306 * sem_init(), sem_trywait(), sem_post(), sem_destroy()
307 * creat(), close(), unlink()
308 * getenv(), putenv()
309 *
310 * Static variables:
311 * m2_file - tmp file name to create ipc key for shared memory
312 * m2_shm_id - shared memory id
313 * m2_shm_sem - shared memory semaphore
314 */
315
316 static char m2_file[MAXPATHLEN];
317 static int m2_shm_id = -1;
318 static sem_t* m2_shm_sem = 0;
319
320 static int
321 m2_init() {
322 char *var;
323 key_t key;
324
325 if ((var = getenv(NOCATGETS("__DMAKE_M2_FILE__"))) == 0) {
326 /* compose tmp file name */
327 sprintf(m2_file, NOCATGETS("%s/dmake.m2.%d.XXXXXX"), tmpdir, getpid());
328
329 /* create tmp file */
330 int fd = mkstemp(m2_file);
331 if (fd < 0) {
332 return -1;
333 } else {
334 close(fd);
335 }
336 } else {
337 /* using existing semaphore */
338 strcpy(m2_file, var);
339 }
340
341 /* combine IPC key */
342 if ((key = ftok(m2_file, 38)) == (key_t) -1) {
343 return -1;
344 }
345
346 /* create shared memory */
347 if ((m2_shm_id = shmget(key, sizeof(*m2_shm_sem), 0666 | (var ? 0 : IPC_CREAT|IPC_EXCL))) == -1) {
349 }
350
351 /* attach shared memory */
352 if ((m2_shm_sem = (sem_t*) shmat(m2_shm_id, 0, 0666)) == (sem_t*)-1) {
353 return -1;
354 }
355
356 /* root process */
357 if (var == 0) {
358 /* initialize semaphore */
359 if (sem_init(m2_shm_sem, 1, pmake_max_jobs)) {
360 return -1;
361 }
362
363 /* alloc memory for env variable */
364 if ((var = (char*) malloc(MAXPATHLEN)) == 0) {
365 return -1;
366 }
367
368 /* put key to env */
369 sprintf(var, NOCATGETS("__DMAKE_M2_FILE__=%s"), m2_file);
370 if (putenv(var)) {
371 return -1;
372 }
373 }
374 return 0;
375 }
376
377 static void
378 m2_fini() {
379 if (m2_shm_id >= 0) {
380 struct shmid_ds stat;
381
382 /* determine the number of attached processes */
383 if (shmctl(m2_shm_id, IPC_STAT, &stat) == 0) {
384 if (stat.shm_nattch <= 1) {
385 /* destroy semaphore */
386 if (m2_shm_sem != 0) {
387 (void) sem_destroy(m2_shm_sem);
388 }
389
464 *
465 * Description:
466 * Prints warning message, cleans up job adjust data, and disables job adjustment
467 *
468 * Environment:
469 * DMAKE_ADJUST_MAX_JOBS
470 *
471 * External functions:
472 * putenv()
473 *
474 * Static variables:
475 * job_adjust_mode Current job adjust mode
476 */
477 static void
478 job_adjust_error() {
479 if (job_adjust_mode != ADJUST_NONE) {
480 /* cleanup internals */
481 job_adjust_fini();
482
483 /* warning message for the user */
484 warning(catgets(catd, 1, 339, "Encountered max jobs auto adjustment error - disabling auto adjustment."));
485
486 /* switch off job adjustment for the children */
487 putenv(strdup(NOCATGETS("DMAKE_ADJUST_MAX_JOBS=NO")));
488
489 /* and for this dmake */
490 job_adjust_mode = ADJUST_NONE;
491 }
492 }
493
494 /*
495 * void job_adjust_init()
496 *
497 * Description:
498 * Parses DMAKE_ADJUST_MAX_JOBS env variable
499 * and performs appropriate initializations.
500 *
501 * Environment:
502 * DMAKE_ADJUST_MAX_JOBS
503 * DMAKE_ADJUST_MAX_JOBS == "NO" - no adjustment
504 * DMAKE_ADJUST_MAX_JOBS == "M2" - M2 adjust mode
505 * other - M1 adjust mode
506 *
507 * External functions:
508 * getenv()
509 *
510 * Static variables:
511 * job_adjust_mode Current job adjust mode
512 */
513 static void
514 job_adjust_init() {
515 if (job_adjust_mode == ADJUST_UNKNOWN) {
516 /* default mode */
517 job_adjust_mode = ADJUST_M1;
518
519 /* determine adjust mode */
520 if (char *var = getenv(NOCATGETS("DMAKE_ADJUST_MAX_JOBS"))) {
521 if (strcasecmp(var, NOCATGETS("NO")) == 0) {
522 job_adjust_mode = ADJUST_NONE;
523 } else if (strcasecmp(var, NOCATGETS("M2")) == 0) {
524 job_adjust_mode = ADJUST_M2;
525 }
526 }
527
528 /* M2 specific initialization */
529 if (job_adjust_mode == ADJUST_M2) {
530 if (m2_init()) {
531 job_adjust_error();
532 }
533 }
534 }
535 }
536
537 #endif /* MAXJOBS_ADJUST_RFE4694000 */
538
539 /*
540 * distribute_process(char **commands, Property line)
541 *
542 * Parameters:
543 * commands argv vector of commands to execute
604 finish_children(true);
605 }
606 }
607 break;
608 default:
609 while (parallel_process_cnt >= pmake_max_jobs) {
610 await_parallel(false);
611 finish_children(true);
612 }
613 }
614 #endif /* TEAMWARE_MAKE_CMN && MAXJOBS_ADJUST_RFE4694000 */
615 setvar_envvar();
616 /*
617 * Tell the user what DMake is doing.
618 */
619 if (!silent && output_mode != txt2_mode) {
620 /*
621 * Print local_host --> x job(s).
622 */
623 (void) fprintf(stdout,
624 catgets(catd, 1, 325, "%s --> %d %s\n"),
625 local_host,
626 parallel_process_cnt + 1,
627 (parallel_process_cnt == 0) ? catgets(catd, 1, 124, "job") : catgets(catd, 1, 125, "jobs"));
628
629 /* Print command line(s). */
630 tmp_index = 0;
631 while (commands[tmp_index] != NULL) {
632 /* No @ char. */
633 /* XXX - need to add [2] when + prefix is added */
634 if ((commands[tmp_index][0] != (int) at_char) &&
635 (commands[tmp_index][1] != (int) at_char)) {
636 tmp_index_str_ptr = commands[tmp_index];
637 if (*tmp_index_str_ptr == (int) hyphen_char) {
638 tmp_index_str_ptr++;
639 }
640 (void) fprintf(stdout, "%s\n", tmp_index_str_ptr);
641 }
642 tmp_index++;
643 }
644 (void) fflush(stdout);
645 }
646
647 (void) sprintf(mbstring,
648 NOCATGETS("%s/dmake.stdout.%d.%d.XXXXXX"),
649 tmpdir,
650 getpid(),
651 file_number++);
652
653 mktemp(mbstring);
654
655 stdout_file = strdup(mbstring);
656 stderr_file = NULL;
657
658 if (!out_err_same) {
659 (void) sprintf(mbstring,
660 NOCATGETS("%s/dmake.stderr.%d.%d.XXXXXX"),
661 tmpdir,
662 getpid(),
663 file_number++);
664
665 mktemp(mbstring);
666
667 stderr_file = strdup(mbstring);
668 }
669
670 process_running = run_rule_commands(local_host, commands);
671
672 return build_running;
673 }
674
675 /*
676 * doname_parallel(target, do_get, implicit)
677 *
678 * Processes the given target and finishes up any parallel
679 * processes left running.
680 *
917 rp->implicit);
918 quiescent = false;
919 delete_running_struct(rp);
920 goto start_loop_3;
921 } else {
922 subtree_target = rp_prev;
923 rp_prev = &rp->next;
924 }
925 } else {
926 rp_prev = &rp->next;
927 }
928 }
929 }
930 /*
931 * If still nothing found to build, we either have a deadlock
932 * or a subtree with a dependency conflict with something waiting
933 * to build.
934 */
935 if (quiescent) {
936 if (subtree_target == NULL) {
937 fatal(catgets(catd, 1, 126, "Internal error: deadlock detected in process_next"));
938 } else {
939 rp = *subtree_target;
940 if (debug_level > 0) {
941 warning(catgets(catd, 1, 127, "Conditional macro conflict encountered for %s between %s and %s"),
942 subtree_conflict2->string_mb,
943 rp->target->string_mb,
944 subtree_conflict->string_mb);
945 }
946 *subtree_target = (*subtree_target)->next;
947 if (rp->next == NULL) {
948 running_tail = subtree_target;
949 }
950 recursion_level = rp->recursion_level;
951 doname_subtree(rp->target, rp->do_get, rp->implicit);
952 delete_running_struct(rp);
953 }
954 }
955 }
956
957 /*
958 * set_conditionals(cnt, targets)
959 *
960 * Sets the conditional macros for the targets given in the array of
961 * targets. The old macro values are returned in an array of
1127 if (!nohang) {
1128 (void) alarm(0);
1129 }
1130 if (pid <= 0) {
1131 if (waiterr == EINTR) {
1132 if (waitflg) {
1133 continue;
1134 } else {
1135 return;
1136 }
1137 } else {
1138 return;
1139 }
1140 }
1141 for (rp = running_list;
1142 (rp != NULL) && (rp->pid != pid);
1143 rp = rp->next) {
1144 ;
1145 }
1146 if (rp == NULL) {
1147 fatal(catgets(catd, 1, 128, "Internal error: returned child pid not in running_list"));
1148 } else {
1149 rp->state = (WIFEXITED(status) && WEXITSTATUS(status) == 0) ? build_ok : build_failed;
1150 }
1151 nohang = true;
1152 parallel_process_cnt--;
1153
1154 #if defined (TEAMWARE_MAKE_CMN) && defined (MAXJOBS_ADJUST_RFE4694000)
1155 if (job_adjust_mode == ADJUST_M2) {
1156 if (m2_release_job()) {
1157 job_adjust_error();
1158 }
1159 }
1160 #endif
1161 }
1162 }
1163
1164 /*
1165 * finish_children(docheck)
1166 *
1167 * Finishes the processing for all targets which were running
1201 * Read the auto dependency stuff, handle a failed build,
1202 * update the target, then finish the doname process for
1203 * that target.
1204 */
1205 if (rp->state == build_ok || rp->state == build_failed) {
1206 *rp_prev = rp->next;
1207 if (rp->next == NULL) {
1208 running_tail = rp_prev;
1209 }
1210 if ((line2 = rp->command) == NULL) {
1211 line2 = get_prop(rp->target->prop, line_prop);
1212 }
1213
1214
1215 /*
1216 * Check if there were any job output
1217 * from the parallel build.
1218 */
1219 if (rp->stdout_file != NULL) {
1220 if (stat(rp->stdout_file, &out_buf) < 0) {
1221 fatal(catgets(catd, 1, 130, "stat of %s failed: %s"),
1222 rp->stdout_file,
1223 errmsg(errno));
1224 }
1225
1226 if ((line2 != NULL) &&
1227 (out_buf.st_size > 0)) {
1228 cmds_length = 0;
1229 for (rule = line2->body.line.command_used,
1230 silent_flag = silent;
1231 rule != NULL;
1232 rule = rule->next) {
1233 cmds_length += rule->command_line->hash.length + 1;
1234 silent_flag = BOOLEAN(silent_flag || rule->silent);
1235 }
1236 if (out_buf.st_size != cmds_length || silent_flag ||
1237 output_mode == txt2_mode) {
1238 dump_out_file(rp->stdout_file, false);
1239 }
1240 }
1241 (void) unlink(rp->stdout_file);
1242 retmem_mb(rp->stdout_file);
1243 rp->stdout_file = NULL;
1244 }
1245
1246 if (!out_err_same && (rp->stderr_file != NULL)) {
1247 if (stat(rp->stderr_file, &out_buf) < 0) {
1248 fatal(catgets(catd, 1, 130, "stat of %s failed: %s"),
1249 rp->stderr_file,
1250 errmsg(errno));
1251 }
1252 if ((line2 != NULL) &&
1253 (out_buf.st_size > 0)) {
1254 dump_out_file(rp->stderr_file, true);
1255 }
1256 (void) unlink(rp->stderr_file);
1257 retmem_mb(rp->stderr_file);
1258 rp->stderr_file = NULL;
1259 }
1260
1261 check_state(rp->temp_file);
1262 if (rp->temp_file != NULL) {
1263 free_name(rp->temp_file);
1264 }
1265 rp->temp_file = NULL;
1266 if (rp->state == build_failed) {
1267 line = get_prop(rp->target->prop, line_prop);
1268 if (line != NULL) {
1269 line->body.line.command_used = NULL;
1270 }
1271 if (continue_after_error ||
1272 fatal_in_progress ||
1273 !docheck) {
1274 warning(catgets(catd, 1, 256, "Command failed for target `%s'"),
1275 rp->command ? line2->body.line.target->string_mb : rp->target->string_mb);
1276 build_failed_seen = true;
1277 } else {
1278 /*
1279 * XXX??? - DMake needs to exit(),
1280 * but shouldn't call fatal().
1281 */
1282 #ifdef PRINT_EXIT_STATUS
1283 warning(NOCATGETS("I'm in finish_children. rp->state == build_failed."));
1284 #endif
1285
1286 fatal(catgets(catd, 1, 258, "Command failed for target `%s'"),
1287 rp->command ? line2->body.line.target->string_mb : rp->target->string_mb);
1288 }
1289 }
1290 if (!docheck) {
1291 delete_running_struct(rp);
1292 rp = *rp_prev;
1293 if (rp == NULL) {
1294 break;
1295 } else {
1296 goto bypass_for_loop_inc_4;
1297 }
1298 }
1299 update_target(get_prop(rp->target->prop, line_prop),
1300 rp->state);
1301 finish_doname(rp);
1302 delete_running_struct(rp);
1303 rp = *rp_prev;
1304 if (rp == NULL) {
1305 break;
1306 } else {
1314
1315 /*
1316 * dump_out_file(filename, err)
1317 *
1318 * Write the contents of the file to stdout, then unlink the file.
1319 *
1320 * Parameters:
1321 * filename Name of temp file containing output
1322 *
1323 * Global variables used:
1324 */
1325 static void
1326 dump_out_file(char *filename, Boolean err)
1327 {
1328 int chars_read;
1329 char copybuf[BUFSIZ];
1330 int fd;
1331 int out_fd = (err ? 2 : 1);
1332
1333 if ((fd = open(filename, O_RDONLY)) < 0) {
1334 fatal(catgets(catd, 1, 141, "open failed for output file %s: %s"),
1335 filename,
1336 errmsg(errno));
1337 }
1338 if (!silent && output_mode != txt2_mode) {
1339 (void) fprintf(err ? stderr : stdout,
1340 err ?
1341 catgets(catd, 1, 338, "%s --> Job errors\n") :
1342 catgets(catd, 1, 259, "%s --> Job output\n"),
1343 local_host);
1344 (void) fflush(err ? stderr : stdout);
1345 }
1346 for (chars_read = read(fd, copybuf, BUFSIZ);
1347 chars_read > 0;
1348 chars_read = read(fd, copybuf, BUFSIZ)) {
1349 /*
1350 * Read buffers from the source file until end or error.
1351 */
1352 if (write(out_fd, copybuf, chars_read) < 0) {
1353 fatal(catgets(catd, 1, 260, "write failed for output file %s: %s"),
1354 filename,
1355 errmsg(errno));
1356 }
1357 }
1358 (void) close(fd);
1359 (void) unlink(filename);
1360 }
1361
1362 /*
1363 * finish_doname(rp)
1364 *
1365 * Completes the processing for a target which was left running.
1366 *
1367 * Parameters:
1368 * rp Running list entry for target
1369 *
1370 * Global variables used:
1371 * debug_level Debug flag
1372 * recursion_level Indentation for debug output
1373 */
1374 static void
1375 finish_doname(Running rp)
1376 {
1377 int auto_count = rp->auto_count;
1378 Name *automatics = rp->automatics;
1379 Doname result = rp->state;
1380 Name target = rp->target;
1381 Name true_target = rp->true_target;
1382 Property *conditionals;
1383
1384 recursion_level = rp->recursion_level;
1385 if (result == build_ok) {
1386 if (true_target == NULL) {
1387 (void) printf(NOCATGETS("Target = %s\n"), target->string_mb);
1388 (void) printf(NOCATGETS(" State = %d\n"), result);
1389 fatal(NOCATGETS("Internal error: NULL true_target in finish_doname"));
1390 }
1391 /* If all went OK, set a nice timestamp */
1392 if (true_target->stat.time == file_doesnt_exist) {
1393 true_target->stat.time = file_max_time;
1394 }
1395 }
1396 target->state = result;
1397 if (target->is_member) {
1398 Property member;
1399
1400 /* Propagate the timestamp from the member file to the member */
1401 if ((target->stat.time != file_max_time) &&
1402 ((member = get_prop(target->prop, member_prop)) != NULL) &&
1403 (exists(member->body.member.member) > file_doesnt_exist)) {
1404 target->stat.time =
1405 /*
1406 exists(member->body.member.member);
1407 */
1408 member->body.member.member->stat.time;
1409 }
1410 }
1411 /*
1412 * Check if we found any new auto dependencies when we
1413 * built the target.
1414 */
1415 if ((result == build_ok) && check_auto_dependencies(target,
1416 auto_count,
1417 automatics)) {
1418 if (debug_level > 0) {
1419 (void) printf(catgets(catd, 1, 261, "%*sTarget `%s' acquired new dependencies from build, checking all dependencies\n"),
1420 recursion_level,
1421 "",
1422 true_target->string_mb);
1423 }
1424 target->rechecking_target = true;
1425 target->state = build_running;
1426
1427 /* [tolik, Tue Mar 25 1997]
1428 * Fix for bug 4038824:
1429 * command line options set by conditional macros get dropped
1430 * rp->conditional_cnt and rp->conditional_targets must be copied
1431 * to new 'rp' during add_pending(). Set_conditionals() stores
1432 * rp->conditional_targets to the global variable 'conditional_targets'
1433 * Add_pending() will use this variable to set up 'rp'.
1434 */
1435 conditionals = set_conditionals(rp->conditional_cnt, rp->conditional_targets);
1436 add_pending(target,
1437 recursion_level,
1438 rp->do_get,
1439 rp->implicit,
1767
1768 /*
1769 * This function replaces the makesh binary.
1770 */
1771
1772
1773 static pid_t
1774 run_rule_commands(char *host, char **commands)
1775 {
1776 Boolean always_exec;
1777 Name command;
1778 Boolean ignore;
1779 int length;
1780 Doname result;
1781 Boolean silent_flag;
1782 wchar_t *tmp_wcs_buffer;
1783
1784 childPid = fork();
1785 switch (childPid) {
1786 case -1: /* Error */
1787 fatal(catgets(catd, 1, 337, "Could not fork child process for dmake job: %s"),
1788 errmsg(errno));
1789 break;
1790 case 0: /* Child */
1791 /* To control the processed targets list is not the child's business */
1792 running_list = NULL;
1793 if(out_err_same) {
1794 redirect_io(stdout_file, (char*)NULL);
1795 } else {
1796 redirect_io(stdout_file, stderr_file);
1797 }
1798 for (commands = commands;
1799 (*commands != (char *)NULL);
1800 commands++) {
1801 silent_flag = silent;
1802 ignore = false;
1803 always_exec = false;
1804 while ((**commands == (int) at_char) ||
1805 (**commands == (int) hyphen_char) ||
1806 (**commands == (int) plus_char)) {
1807 if (**commands == (int) at_char) {
1820 (void) mbstowcs(tmp_wcs_buffer, *commands, length + 1);
1821 command = GETNAME(tmp_wcs_buffer, FIND_LENGTH);
1822 retmem(tmp_wcs_buffer);
1823 } else {
1824 MBSTOWCS(wcs_buffer, *commands);
1825 command = GETNAME(wcs_buffer, FIND_LENGTH);
1826 }
1827 if ((command->hash.length > 0) &&
1828 !silent_flag) {
1829 (void) printf("%s\n", command->string_mb);
1830 }
1831 result = dosys(command,
1832 ignore,
1833 false,
1834 false, /* bugs #4085164 & #4990057 */
1835 /* BOOLEAN(silent_flag && ignore), */
1836 always_exec,
1837 (Name) NULL);
1838 if (result == build_failed) {
1839 if (silent_flag) {
1840 (void) printf(catgets(catd, 1, 152, "The following command caused the error:\n%s\n"), command->string_mb);
1841 }
1842 if (!ignore) {
1843 _exit(1);
1844 }
1845 }
1846 }
1847 _exit(0);
1848 break;
1849 default:
1850 break;
1851 }
1852 return childPid;
1853 }
1854
1855 static void
1856 maybe_reread_make_state(void)
1857 {
1858 /* Copying dosys()... */
1859 if (report_dependencies_level == 0) {
1860 make_state->stat.time = file_no_time;
|
29 *
30 * Deal with the parallel processing
31 */
32
33 /*
34 * Included files
35 */
36 #include <errno.h> /* errno */
37 #include <fcntl.h>
38 #include <mk/defs.h>
39 #include <mksh/dosys.h> /* redirect_io() */
40 #include <mksh/macro.h> /* expand_value() */
41 #include <mksh/misc.h> /* getmem() */
42 #include <sys/signal.h>
43 #include <sys/stat.h>
44 #include <sys/types.h>
45 #include <sys/utsname.h>
46 #include <sys/wait.h>
47 #include <unistd.h>
48 #include <netdb.h>
49 #include <libintl.h>
50
51
52
53 /*
54 * Defined macros
55 */
56 #define MAXRULES 100
57
58 /*
59 * This const should be in avo_dms/include/AvoDmakeCommand.h
60 */
61 const int local_host_mask = 0x20;
62
63
64 /*
65 * typedefs & structs
66 */
67
68
69 /*
115 int cmd_options = 0;
116 char *commands[MAXRULES + 5];
117 char *cp;
118 Name dmake_name;
119 Name dmake_value;
120 int ignore;
121 Name make_machines_name;
122 char **p;
123 Property prop;
124 Doname result = build_ok;
125 Cmd_line rule;
126 Boolean silent_flag;
127 Name target = line->body.line.target;
128 Boolean wrote_state_file = false;
129
130 if ((pmake_max_jobs == 0) &&
131 (dmake_mode_type == parallel_mode)) {
132 if (local_host[0] == '\0') {
133 (void) gethostname(local_host, MAXNAMELEN);
134 }
135 MBSTOWCS(wcs_buffer, "DMAKE_MAX_JOBS");
136 dmake_name = GETNAME(wcs_buffer, FIND_LENGTH);
137 if (((prop = get_prop(dmake_name->prop, macro_prop)) != NULL) &&
138 ((dmake_value = prop->body.macro.value) != NULL)) {
139 pmake_max_jobs = atoi(dmake_value->string_mb);
140 if (pmake_max_jobs <= 0) {
141 warning(gettext("DMAKE_MAX_JOBS cannot be less than or equal to zero."));
142 warning(gettext("setting DMAKE_MAX_JOBS to %d."), PMAKE_DEF_MAX_JOBS);
143 pmake_max_jobs = PMAKE_DEF_MAX_JOBS;
144 }
145 } else {
146 /*
147 * For backwards compatibility w/ PMake 1.x, when
148 * DMake 2.x is being run in parallel mode, DMake
149 * should parse the PMake startup file
150 * $(HOME)/.make.machines to get the pmake_max_jobs.
151 */
152 MBSTOWCS(wcs_buffer, "PMAKE_MACHINESFILE");
153 dmake_name = GETNAME(wcs_buffer, FIND_LENGTH);
154 if (((prop = get_prop(dmake_name->prop, macro_prop)) != NULL) &&
155 ((dmake_value = prop->body.macro.value) != NULL)) {
156 make_machines_name = dmake_value;
157 } else {
158 make_machines_name = NULL;
159 }
160 if ((pmake_max_jobs = read_make_machines(make_machines_name)) <= 0) {
161 pmake_max_jobs = PMAKE_DEF_MAX_JOBS;
162 }
163 }
164 }
165
166 if ((dmake_mode_type == serial_mode) ||
167 ((dmake_mode_type == parallel_mode) && (waitflg))) {
168 return (execute_serial(line));
169 }
170
171 {
172 p = commands;
306 * ftok(), shmget(), shmat(), shmdt(), shmctl()
307 * sem_init(), sem_trywait(), sem_post(), sem_destroy()
308 * creat(), close(), unlink()
309 * getenv(), putenv()
310 *
311 * Static variables:
312 * m2_file - tmp file name to create ipc key for shared memory
313 * m2_shm_id - shared memory id
314 * m2_shm_sem - shared memory semaphore
315 */
316
317 static char m2_file[MAXPATHLEN];
318 static int m2_shm_id = -1;
319 static sem_t* m2_shm_sem = 0;
320
321 static int
322 m2_init() {
323 char *var;
324 key_t key;
325
326 if ((var = getenv("__DMAKE_M2_FILE__")) == 0) {
327 /* compose tmp file name */
328 sprintf(m2_file, "%s/dmake.m2.%d.XXXXXX", tmpdir, getpid());
329
330 /* create tmp file */
331 int fd = mkstemp(m2_file);
332 if (fd < 0) {
333 return -1;
334 } else {
335 close(fd);
336 }
337 } else {
338 /* using existing semaphore */
339 strcpy(m2_file, var);
340 }
341
342 /* combine IPC key */
343 if ((key = ftok(m2_file, 38)) == (key_t) -1) {
344 return -1;
345 }
346
347 /* create shared memory */
348 if ((m2_shm_id = shmget(key, sizeof(*m2_shm_sem), 0666 | (var ? 0 : IPC_CREAT|IPC_EXCL))) == -1) {
350 }
351
352 /* attach shared memory */
353 if ((m2_shm_sem = (sem_t*) shmat(m2_shm_id, 0, 0666)) == (sem_t*)-1) {
354 return -1;
355 }
356
357 /* root process */
358 if (var == 0) {
359 /* initialize semaphore */
360 if (sem_init(m2_shm_sem, 1, pmake_max_jobs)) {
361 return -1;
362 }
363
364 /* alloc memory for env variable */
365 if ((var = (char*) malloc(MAXPATHLEN)) == 0) {
366 return -1;
367 }
368
369 /* put key to env */
370 sprintf(var, "__DMAKE_M2_FILE__=%s", m2_file);
371 if (putenv(var)) {
372 return -1;
373 }
374 }
375 return 0;
376 }
377
378 static void
379 m2_fini() {
380 if (m2_shm_id >= 0) {
381 struct shmid_ds stat;
382
383 /* determine the number of attached processes */
384 if (shmctl(m2_shm_id, IPC_STAT, &stat) == 0) {
385 if (stat.shm_nattch <= 1) {
386 /* destroy semaphore */
387 if (m2_shm_sem != 0) {
388 (void) sem_destroy(m2_shm_sem);
389 }
390
465 *
466 * Description:
467 * Prints warning message, cleans up job adjust data, and disables job adjustment
468 *
469 * Environment:
470 * DMAKE_ADJUST_MAX_JOBS
471 *
472 * External functions:
473 * putenv()
474 *
475 * Static variables:
476 * job_adjust_mode Current job adjust mode
477 */
478 static void
479 job_adjust_error() {
480 if (job_adjust_mode != ADJUST_NONE) {
481 /* cleanup internals */
482 job_adjust_fini();
483
484 /* warning message for the user */
485 warning(gettext("Encountered max jobs auto adjustment error - disabling auto adjustment."));
486
487 /* switch off job adjustment for the children */
488 putenv(strdup("DMAKE_ADJUST_MAX_JOBS=NO"));
489
490 /* and for this dmake */
491 job_adjust_mode = ADJUST_NONE;
492 }
493 }
494
495 /*
496 * void job_adjust_init()
497 *
498 * Description:
499 * Parses DMAKE_ADJUST_MAX_JOBS env variable
500 * and performs appropriate initializations.
501 *
502 * Environment:
503 * DMAKE_ADJUST_MAX_JOBS
504 * DMAKE_ADJUST_MAX_JOBS == "NO" - no adjustment
505 * DMAKE_ADJUST_MAX_JOBS == "M2" - M2 adjust mode
506 * other - M1 adjust mode
507 *
508 * External functions:
509 * getenv()
510 *
511 * Static variables:
512 * job_adjust_mode Current job adjust mode
513 */
514 static void
515 job_adjust_init() {
516 if (job_adjust_mode == ADJUST_UNKNOWN) {
517 /* default mode */
518 job_adjust_mode = ADJUST_M1;
519
520 /* determine adjust mode */
521 if (char *var = getenv("DMAKE_ADJUST_MAX_JOBS")) {
522 if (strcasecmp(var, "NO") == 0) {
523 job_adjust_mode = ADJUST_NONE;
524 } else if (strcasecmp(var, "M2") == 0) {
525 job_adjust_mode = ADJUST_M2;
526 }
527 }
528
529 /* M2 specific initialization */
530 if (job_adjust_mode == ADJUST_M2) {
531 if (m2_init()) {
532 job_adjust_error();
533 }
534 }
535 }
536 }
537
538 #endif /* MAXJOBS_ADJUST_RFE4694000 */
539
540 /*
541 * distribute_process(char **commands, Property line)
542 *
543 * Parameters:
544 * commands argv vector of commands to execute
605 finish_children(true);
606 }
607 }
608 break;
609 default:
610 while (parallel_process_cnt >= pmake_max_jobs) {
611 await_parallel(false);
612 finish_children(true);
613 }
614 }
615 #endif /* TEAMWARE_MAKE_CMN && MAXJOBS_ADJUST_RFE4694000 */
616 setvar_envvar();
617 /*
618 * Tell the user what DMake is doing.
619 */
620 if (!silent && output_mode != txt2_mode) {
621 /*
622 * Print local_host --> x job(s).
623 */
624 (void) fprintf(stdout,
625 gettext("%s --> %d %s\n"),
626 local_host,
627 parallel_process_cnt + 1,
628 (parallel_process_cnt == 0) ? gettext("job") : gettext("jobs"));
629
630 /* Print command line(s). */
631 tmp_index = 0;
632 while (commands[tmp_index] != NULL) {
633 /* No @ char. */
634 /* XXX - need to add [2] when + prefix is added */
635 if ((commands[tmp_index][0] != (int) at_char) &&
636 (commands[tmp_index][1] != (int) at_char)) {
637 tmp_index_str_ptr = commands[tmp_index];
638 if (*tmp_index_str_ptr == (int) hyphen_char) {
639 tmp_index_str_ptr++;
640 }
641 (void) fprintf(stdout, "%s\n", tmp_index_str_ptr);
642 }
643 tmp_index++;
644 }
645 (void) fflush(stdout);
646 }
647
648 (void) sprintf(mbstring,
649 "%s/dmake.stdout.%d.%d.XXXXXX",
650 tmpdir,
651 getpid(),
652 file_number++);
653
654 mktemp(mbstring);
655
656 stdout_file = strdup(mbstring);
657 stderr_file = NULL;
658
659 if (!out_err_same) {
660 (void) sprintf(mbstring,
661 "%s/dmake.stderr.%d.%d.XXXXXX",
662 tmpdir,
663 getpid(),
664 file_number++);
665
666 mktemp(mbstring);
667
668 stderr_file = strdup(mbstring);
669 }
670
671 process_running = run_rule_commands(local_host, commands);
672
673 return build_running;
674 }
675
676 /*
677 * doname_parallel(target, do_get, implicit)
678 *
679 * Processes the given target and finishes up any parallel
680 * processes left running.
681 *
918 rp->implicit);
919 quiescent = false;
920 delete_running_struct(rp);
921 goto start_loop_3;
922 } else {
923 subtree_target = rp_prev;
924 rp_prev = &rp->next;
925 }
926 } else {
927 rp_prev = &rp->next;
928 }
929 }
930 }
931 /*
932 * If still nothing found to build, we either have a deadlock
933 * or a subtree with a dependency conflict with something waiting
934 * to build.
935 */
936 if (quiescent) {
937 if (subtree_target == NULL) {
938 fatal(gettext("Internal error: deadlock detected in process_next"));
939 } else {
940 rp = *subtree_target;
941 if (debug_level > 0) {
942 warning(gettext("Conditional macro conflict encountered for %s between %s and %s"),
943 subtree_conflict2->string_mb,
944 rp->target->string_mb,
945 subtree_conflict->string_mb);
946 }
947 *subtree_target = (*subtree_target)->next;
948 if (rp->next == NULL) {
949 running_tail = subtree_target;
950 }
951 recursion_level = rp->recursion_level;
952 doname_subtree(rp->target, rp->do_get, rp->implicit);
953 delete_running_struct(rp);
954 }
955 }
956 }
957
958 /*
959 * set_conditionals(cnt, targets)
960 *
961 * Sets the conditional macros for the targets given in the array of
962 * targets. The old macro values are returned in an array of
1128 if (!nohang) {
1129 (void) alarm(0);
1130 }
1131 if (pid <= 0) {
1132 if (waiterr == EINTR) {
1133 if (waitflg) {
1134 continue;
1135 } else {
1136 return;
1137 }
1138 } else {
1139 return;
1140 }
1141 }
1142 for (rp = running_list;
1143 (rp != NULL) && (rp->pid != pid);
1144 rp = rp->next) {
1145 ;
1146 }
1147 if (rp == NULL) {
1148 fatal(gettext("Internal error: returned child pid not in running_list"));
1149 } else {
1150 rp->state = (WIFEXITED(status) && WEXITSTATUS(status) == 0) ? build_ok : build_failed;
1151 }
1152 nohang = true;
1153 parallel_process_cnt--;
1154
1155 #if defined (TEAMWARE_MAKE_CMN) && defined (MAXJOBS_ADJUST_RFE4694000)
1156 if (job_adjust_mode == ADJUST_M2) {
1157 if (m2_release_job()) {
1158 job_adjust_error();
1159 }
1160 }
1161 #endif
1162 }
1163 }
1164
1165 /*
1166 * finish_children(docheck)
1167 *
1168 * Finishes the processing for all targets which were running
1202 * Read the auto dependency stuff, handle a failed build,
1203 * update the target, then finish the doname process for
1204 * that target.
1205 */
1206 if (rp->state == build_ok || rp->state == build_failed) {
1207 *rp_prev = rp->next;
1208 if (rp->next == NULL) {
1209 running_tail = rp_prev;
1210 }
1211 if ((line2 = rp->command) == NULL) {
1212 line2 = get_prop(rp->target->prop, line_prop);
1213 }
1214
1215
1216 /*
1217 * Check if there were any job output
1218 * from the parallel build.
1219 */
1220 if (rp->stdout_file != NULL) {
1221 if (stat(rp->stdout_file, &out_buf) < 0) {
1222 fatal(gettext("stat of %s failed: %s"),
1223 rp->stdout_file,
1224 errmsg(errno));
1225 }
1226
1227 if ((line2 != NULL) &&
1228 (out_buf.st_size > 0)) {
1229 cmds_length = 0;
1230 for (rule = line2->body.line.command_used,
1231 silent_flag = silent;
1232 rule != NULL;
1233 rule = rule->next) {
1234 cmds_length += rule->command_line->hash.length + 1;
1235 silent_flag = BOOLEAN(silent_flag || rule->silent);
1236 }
1237 if (out_buf.st_size != cmds_length || silent_flag ||
1238 output_mode == txt2_mode) {
1239 dump_out_file(rp->stdout_file, false);
1240 }
1241 }
1242 (void) unlink(rp->stdout_file);
1243 retmem_mb(rp->stdout_file);
1244 rp->stdout_file = NULL;
1245 }
1246
1247 if (!out_err_same && (rp->stderr_file != NULL)) {
1248 if (stat(rp->stderr_file, &out_buf) < 0) {
1249 fatal(gettext("stat of %s failed: %s"),
1250 rp->stderr_file,
1251 errmsg(errno));
1252 }
1253 if ((line2 != NULL) &&
1254 (out_buf.st_size > 0)) {
1255 dump_out_file(rp->stderr_file, true);
1256 }
1257 (void) unlink(rp->stderr_file);
1258 retmem_mb(rp->stderr_file);
1259 rp->stderr_file = NULL;
1260 }
1261
1262 check_state(rp->temp_file);
1263 if (rp->temp_file != NULL) {
1264 free_name(rp->temp_file);
1265 }
1266 rp->temp_file = NULL;
1267 if (rp->state == build_failed) {
1268 line = get_prop(rp->target->prop, line_prop);
1269 if (line != NULL) {
1270 line->body.line.command_used = NULL;
1271 }
1272 if (continue_after_error ||
1273 fatal_in_progress ||
1274 !docheck) {
1275 warning(gettext("Command failed for target `%s'"),
1276 rp->command ? line2->body.line.target->string_mb : rp->target->string_mb);
1277 build_failed_seen = true;
1278 } else {
1279 /*
1280 * XXX??? - DMake needs to exit(),
1281 * but shouldn't call fatal().
1282 */
1283 #ifdef PRINT_EXIT_STATUS
1284 warning("I'm in finish_children. rp->state == build_failed.");
1285 #endif
1286
1287 fatal(gettext("Command failed for target `%s'"),
1288 rp->command ? line2->body.line.target->string_mb : rp->target->string_mb);
1289 }
1290 }
1291 if (!docheck) {
1292 delete_running_struct(rp);
1293 rp = *rp_prev;
1294 if (rp == NULL) {
1295 break;
1296 } else {
1297 goto bypass_for_loop_inc_4;
1298 }
1299 }
1300 update_target(get_prop(rp->target->prop, line_prop),
1301 rp->state);
1302 finish_doname(rp);
1303 delete_running_struct(rp);
1304 rp = *rp_prev;
1305 if (rp == NULL) {
1306 break;
1307 } else {
1315
1316 /*
1317 * dump_out_file(filename, err)
1318 *
1319 * Write the contents of the file to stdout, then unlink the file.
1320 *
1321 * Parameters:
1322 * filename Name of temp file containing output
1323 *
1324 * Global variables used:
1325 */
1326 static void
1327 dump_out_file(char *filename, Boolean err)
1328 {
1329 int chars_read;
1330 char copybuf[BUFSIZ];
1331 int fd;
1332 int out_fd = (err ? 2 : 1);
1333
1334 if ((fd = open(filename, O_RDONLY)) < 0) {
1335 fatal(gettext("open failed for output file %s: %s"),
1336 filename,
1337 errmsg(errno));
1338 }
1339 if (!silent && output_mode != txt2_mode) {
1340 (void) fprintf(err ? stderr : stdout,
1341 err ?
1342 gettext("%s --> Job errors\n") :
1343 gettext("%s --> Job output\n"),
1344 local_host);
1345 (void) fflush(err ? stderr : stdout);
1346 }
1347 for (chars_read = read(fd, copybuf, BUFSIZ);
1348 chars_read > 0;
1349 chars_read = read(fd, copybuf, BUFSIZ)) {
1350 /*
1351 * Read buffers from the source file until end or error.
1352 */
1353 if (write(out_fd, copybuf, chars_read) < 0) {
1354 fatal(gettext("write failed for output file %s: %s"),
1355 filename,
1356 errmsg(errno));
1357 }
1358 }
1359 (void) close(fd);
1360 (void) unlink(filename);
1361 }
1362
1363 /*
1364 * finish_doname(rp)
1365 *
1366 * Completes the processing for a target which was left running.
1367 *
1368 * Parameters:
1369 * rp Running list entry for target
1370 *
1371 * Global variables used:
1372 * debug_level Debug flag
1373 * recursion_level Indentation for debug output
1374 */
1375 static void
1376 finish_doname(Running rp)
1377 {
1378 int auto_count = rp->auto_count;
1379 Name *automatics = rp->automatics;
1380 Doname result = rp->state;
1381 Name target = rp->target;
1382 Name true_target = rp->true_target;
1383 Property *conditionals;
1384
1385 recursion_level = rp->recursion_level;
1386 if (result == build_ok) {
1387 if (true_target == NULL) {
1388 (void) printf("Target = %s\n", target->string_mb);
1389 (void) printf(" State = %d\n", result);
1390 fatal("Internal error: NULL true_target in finish_doname");
1391 }
1392 /* If all went OK, set a nice timestamp */
1393 if (true_target->stat.time == file_doesnt_exist) {
1394 true_target->stat.time = file_max_time;
1395 }
1396 }
1397 target->state = result;
1398 if (target->is_member) {
1399 Property member;
1400
1401 /* Propagate the timestamp from the member file to the member */
1402 if ((target->stat.time != file_max_time) &&
1403 ((member = get_prop(target->prop, member_prop)) != NULL) &&
1404 (exists(member->body.member.member) > file_doesnt_exist)) {
1405 target->stat.time =
1406 /*
1407 exists(member->body.member.member);
1408 */
1409 member->body.member.member->stat.time;
1410 }
1411 }
1412 /*
1413 * Check if we found any new auto dependencies when we
1414 * built the target.
1415 */
1416 if ((result == build_ok) && check_auto_dependencies(target,
1417 auto_count,
1418 automatics)) {
1419 if (debug_level > 0) {
1420 (void) printf(gettext("%*sTarget `%s' acquired new dependencies from build, checking all dependencies\n"),
1421 recursion_level,
1422 "",
1423 true_target->string_mb);
1424 }
1425 target->rechecking_target = true;
1426 target->state = build_running;
1427
1428 /* [tolik, Tue Mar 25 1997]
1429 * Fix for bug 4038824:
1430 * command line options set by conditional macros get dropped
1431 * rp->conditional_cnt and rp->conditional_targets must be copied
1432 * to new 'rp' during add_pending(). Set_conditionals() stores
1433 * rp->conditional_targets to the global variable 'conditional_targets'
1434 * Add_pending() will use this variable to set up 'rp'.
1435 */
1436 conditionals = set_conditionals(rp->conditional_cnt, rp->conditional_targets);
1437 add_pending(target,
1438 recursion_level,
1439 rp->do_get,
1440 rp->implicit,
1768
1769 /*
1770 * This function replaces the makesh binary.
1771 */
1772
1773
1774 static pid_t
1775 run_rule_commands(char *host, char **commands)
1776 {
1777 Boolean always_exec;
1778 Name command;
1779 Boolean ignore;
1780 int length;
1781 Doname result;
1782 Boolean silent_flag;
1783 wchar_t *tmp_wcs_buffer;
1784
1785 childPid = fork();
1786 switch (childPid) {
1787 case -1: /* Error */
1788 fatal(gettext("Could not fork child process for dmake job: %s"),
1789 errmsg(errno));
1790 break;
1791 case 0: /* Child */
1792 /* To control the processed targets list is not the child's business */
1793 running_list = NULL;
1794 if(out_err_same) {
1795 redirect_io(stdout_file, (char*)NULL);
1796 } else {
1797 redirect_io(stdout_file, stderr_file);
1798 }
1799 for (commands = commands;
1800 (*commands != (char *)NULL);
1801 commands++) {
1802 silent_flag = silent;
1803 ignore = false;
1804 always_exec = false;
1805 while ((**commands == (int) at_char) ||
1806 (**commands == (int) hyphen_char) ||
1807 (**commands == (int) plus_char)) {
1808 if (**commands == (int) at_char) {
1821 (void) mbstowcs(tmp_wcs_buffer, *commands, length + 1);
1822 command = GETNAME(tmp_wcs_buffer, FIND_LENGTH);
1823 retmem(tmp_wcs_buffer);
1824 } else {
1825 MBSTOWCS(wcs_buffer, *commands);
1826 command = GETNAME(wcs_buffer, FIND_LENGTH);
1827 }
1828 if ((command->hash.length > 0) &&
1829 !silent_flag) {
1830 (void) printf("%s\n", command->string_mb);
1831 }
1832 result = dosys(command,
1833 ignore,
1834 false,
1835 false, /* bugs #4085164 & #4990057 */
1836 /* BOOLEAN(silent_flag && ignore), */
1837 always_exec,
1838 (Name) NULL);
1839 if (result == build_failed) {
1840 if (silent_flag) {
1841 (void) printf(gettext("The following command caused the error:\n%s\n"), command->string_mb);
1842 }
1843 if (!ignore) {
1844 _exit(1);
1845 }
1846 }
1847 }
1848 _exit(0);
1849 break;
1850 default:
1851 break;
1852 }
1853 return childPid;
1854 }
1855
1856 static void
1857 maybe_reread_make_state(void)
1858 {
1859 /* Copying dosys()... */
1860 if (report_dependencies_level == 0) {
1861 make_state->stat.time = file_no_time;
|