1 /* COPYING ******************************************************************
2 For copyright and licensing terms, see the file named COPYING.
3 // **************************************************************************
4 */
5 
6 #include <string>
7 #include <list>
8 #include <memory>
9 #include <map>
10 #include <vector>
11 #include <iostream>
12 #include <iomanip>
13 #include <fstream>
14 #include <sstream>
15 #include <cassert>
16 #include <cctype>
17 #include <cstring>
18 #include <cstdio>
19 #include <cstdlib>
20 #include <climits>
21 #include <cerrno>
22 #include <ctime>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <fcntl.h>
26 #include <unistd.h>
27 #include <ftw.h>
28 #include "popt.h"
29 #include "CubeHash.h"
30 #include "redo.h"
31 #include "jobserver.h"
32 #include "lockfile.h"
33 #include "wait.h"
34 #include "FileDescriptorOwner.h"
35 #if defined(__unix__) || defined(__UNIX__) || (defined(__APPLE__) && defined(__MACH__)) || defined(__INTERIX)
36 extern "C" char ** environ;
37 #endif
38 
39 bool debug(false);
40 
41 namespace {
42 
43 struct catchall_definition : public popt::definition {
44 public:
catchall_definition__anon5955c6770111::catchall_definition45 	catchall_definition() {}
execute__anon5955c6770111::catchall_definition46 	virtual bool execute(popt::processor &, char) { return true; }
47 	// We'll catch this later when it is re-tried as a short option on its own.
execute__anon5955c6770111::catchall_definition48 	virtual bool execute(popt::processor &, char, const char *) { return false; }
execute__anon5955c6770111::catchall_definition49 	virtual bool execute(popt::processor &, const char *) { return true; }
~catchall_definition__anon5955c6770111::catchall_definition50 	virtual ~catchall_definition() {}
51 };
52 
53 struct special_string_definition : public popt::string_definition {
54 public:
special_string_definition__anon5955c6770111::special_string_definition55 	special_string_definition(char s, const char * l, const char * a, const char * d, const char * & v) : string_definition(s, l, a, d, v) {}
execute__anon5955c6770111::special_string_definition56 	virtual bool execute(popt::processor & proc, const char * s)
57 	{
58 		if (const char * long_name = query_long_name()) {
59 			const std::size_t len(std::strlen(long_name));
60 			if (0 == std::memcmp(long_name, s, len) && '=' == s[len]) {
61 				action(proc, s + len + 1);
62 				return true;
63 			}
64 		}
65 		return false;
66 	}
67 };
68 
69 enum { MAX_META_DEPTH = 1U };
70 bool keep_going(false);
71 bool silent(false);
72 bool verbose(false);
73 int redoparent_fd = -1;
74 std::string makelevel;
75 
76 }
77 
78 extern
79 std::ostream &
msg(const char * prog,const char * prefix)80 msg (
81 	const char * prog,
82 	const char * prefix
83 ) {
84 	std::clog << prog;
85 	if (!makelevel.empty())
86 		std::clog << "[" << makelevel << "]";
87 	return std::clog << ": " << prefix << ": ";
88 }
89 
90 /* Filename manipulation ****************************************************
91 // **************************************************************************
92 */
93 
94 static inline
95 const char *
extension(const char * s)96 extension (
97 	const char * s
98 ) {
99 	if (const char * dot = std::strchr(s, '.'))
100 		return dot;
101 	return std::strchr(s, '\0');
102 }
103 
104 static inline
105 const char *
basename_of(const char * s)106 basename_of (
107 	const char * s
108 ) {
109 	if (const char * slash = std::strrchr(s, '/'))
110 		s = slash + 1;
111 	return s;
112 }
113 
114 static inline
115 bool
is_root(const char * path)116 is_root(const char *path)
117 {
118 	return ('/' == path[0]) && !path[1];
119 }
120 
121 static inline
122 bool
is_dot_or_dotdot(const char * path)123 is_dot_or_dotdot(const char *path)
124 {
125 	return !path[0] || (('.' == path[0]) && (!path[1] || (('.' == path[1]) && !path[2])));
126 }
127 
128 static inline
129 bool
is_root_or_ends_with_dot_or_dotdot(const char * path)130 is_root_or_ends_with_dot_or_dotdot(const char *path)
131 {
132 	return is_root(path) || is_dot_or_dotdot(basename_of(path));
133 }
134 
135 /* Wrappers for POSIX API calls. ********************************************
136 // **************************************************************************
137 */
138 
139 static
140 int
unlink_cb(const char * fpath,const struct stat *,int typeflag,struct FTW *)141 unlink_cb(const char *fpath, const struct stat *, int typeflag, struct FTW *)
142 {
143 	switch (typeflag) {
144 		case FTW_D:
145 		case FTW_DP:
146 		case FTW_DNR:
147 		case FTW_F:
148 		case FTW_SL:
149 			return std::remove(fpath);
150 		default:
151 			return static_cast<void>(errno = EINVAL), -1;
152 	}
153 }
154 
155 static inline
156 int
rmrf(const char * path)157 rmrf(const char *path)
158 {
159 	if (is_root_or_ends_with_dot_or_dotdot(path))
160 		return static_cast<void>(errno = EINVAL), -1;
161 	struct stat stbuf;
162 	if (0 <= lstat(path, &stbuf) && S_ISDIR(stbuf.st_mode))
163 		return nftw(path, unlink_cb, 64, FTW_DEPTH | FTW_PHYS);
164 	else
165 		return std::remove(path);
166 }
167 
168 static inline
169 int
spawn(const char * prog,const char * argv[],const char * envv[])170 spawn (
171 	const char * prog,
172 	const char * argv[],
173 	const char * envv[]
174 ) {
175 	std::clog << std::flush;
176 	int pid = fork();
177 	if (0 != pid)
178 		return pid;
179 	execve(prog, const_cast<char **>(argv), const_cast<char **>(envv));
180 	const int error(errno);
181 	std::clog << prog << ": " << std::strerror(error) << '\n' << std::flush;
182 	_exit(255);
183 }
184 
185 static
186 void
makepath(const std::string & dir)187 makepath (
188 	const std::string & dir
189 ) {
190 	const char * arg(dir.c_str());
191 	const char * b(basename_of(arg));
192 	if (b != arg)
193 		makepath(std::string(arg, static_cast<std::size_t>(b - 1 - arg)));
194 	mkdir(arg, 0777);
195 }
196 
197 static inline
198 bool
exists(const std::string & name)199 exists(
200 	const std::string & name
201 ) {
202 	return 0 <= access(name.c_str(), F_OK);
203 }
204 
205 /* string manipulation ******************************************************
206 // **************************************************************************
207 */
208 
209 static inline
210 int
x2d(char c)211 x2d (
212 	char c
213 ) {
214 	if (!std::isxdigit(c)) return -1;
215 	if (std::isdigit(c)) return c - '0';
216 	if (std::isupper(c)) return c - 'A' + 10;
217 	if (std::islower(c)) return c - 'a' + 10;
218 	return -1;
219 }
220 
221 static inline
222 std::list<std::string>
split(const char * s,bool prefixdash)223 split(
224 	const char * s,
225 	bool prefixdash
226 ) {
227 	std::list<std::string> l;
228 	if (prefixdash && *s && std::isspace(*s)) prefixdash = false;
229 	for (;;) {
230 		while (!prefixdash && *s && std::isspace(*s)) ++s;
231 		if (!*s) break;
232 		std::string r(prefixdash ? "-" : "");
233 		prefixdash = false;
234 		bool quoted(false);
235 		while (char c = *s) {
236 			if ('\"' == c)
237 				quoted = !quoted;
238 			else if ('\\' == c) {
239 				c = *++s;
240 				if (c)
241 					r += c;
242 				else {
243 					r += '\\';
244 					break;
245 				}
246 			} else if (!quoted && std::isspace(c))
247 				break;
248 			else
249 				r += c;
250 			++s;
251 		}
252 		l.push_back(r);
253 	}
254 	return l;
255 }
256 
257 static
258 std::vector<const char *>
convert(const std::list<std::string> & s)259 convert (
260 	const std::list<std::string> & s
261 ) {
262 	std::vector<const char *> v;
263 	for (std::list<std::string>::const_iterator i(s.begin()); s.end() != i; ++i)
264 		v.push_back(i->c_str());
265 	return v;
266 }
267 
268 /* struct Information and the cache of directory entry information **********
269 // **************************************************************************
270 */
271 
272 namespace {
273 
274 struct Information {
275 	enum { NOTHING, SPECIAL, DIRECTORY, FILE } type;
276 	std::time_t last_written;
277 	unsigned char hash[32];		// 256 bits
278 };
279 
280 typedef std::map<std::string, Information> InfoMap;
281 InfoMap file_info_map;
282 
283 }
284 
285 static inline
286 Information
read_file_info(const std::string & name,const Information * old_info)287 read_file_info (
288 	const std::string & name,
289 	const Information * old_info
290 ) {
291 	Information i;
292 	struct stat stbuf;
293 	if (0 > lstat(name.c_str(), &stbuf)) {
294 		i.type = i.NOTHING;
295 		i.last_written = -1;
296 		for ( std::size_t j(0);j < sizeof i.hash/sizeof *i.hash; ++j)
297 			i.hash[j] = 0;
298 	} else {
299 		i.last_written = stbuf.st_mtime;
300 		if (S_ISREG(stbuf.st_mode)) {
301 			i.type = i.FILE;
302 			if (old_info && old_info->last_written == i.last_written) {
303 				memmove(i.hash, old_info->hash, sizeof i.hash);
304 			} else {
305 				// This is Dan Bernstein's SHA-3-AHS256 proposal from 2010-11.
306 				CubeHash h(16U, 16U, 32U, 32U, 8U * sizeof i.hash/sizeof *i.hash);
307 				std::ifstream f(name.c_str(), std::ios::binary);
308 				if (!f.fail()) {
309 					char buf[4096];
310 					for (;;) {
311 						f.read(buf, sizeof buf);
312 						h.Update(reinterpret_cast<unsigned char *>(buf), static_cast<std::size_t>(f.gcount()));
313 						if (f.eof()) break;
314 					}
315 				}
316 				h.Final();
317 				for ( std::size_t j(0);j < sizeof i.hash/sizeof *i.hash; ++j)
318 					i.hash[j] = h.hashval[j];
319 			}
320 		} else {
321 			if (S_ISDIR(stbuf.st_mode))
322 				i.type = i.DIRECTORY;
323 			else
324 				i.type = i.SPECIAL;
325 			for ( std::size_t j(0);j < sizeof i.hash/sizeof *i.hash; ++j)
326 				i.hash[j] = 0;
327 		}
328 	}
329 	return i;
330 }
331 
332 static
333 Information &
get_file_info(const std::string & name,const Information * old_info)334 get_file_info (
335 	const std::string & name,
336 	const Information * old_info
337 ) {
338 	InfoMap::iterator f(file_info_map.find(name));
339 	if (f == file_info_map.end())
340 		f = file_info_map.insert(InfoMap::value_type(name, read_file_info(name, old_info))).first;
341 	return f->second;
342 }
343 
344 static inline
345 void
delete_file_info(const std::string & name)346 delete_file_info (
347 	const std::string & name
348 ) {
349 	file_info_map.erase(name);
350 }
351 
352 /* Parsing and updating of the .redo database *******************************
353 // **************************************************************************
354 */
355 
356 static inline
357 void
read_db_line(std::istream & s,Information & i,std::string & name)358 read_db_line (
359 	std::istream & s,
360 	Information & i,
361 	std::string & name
362 ) {
363 	switch (int c = s.get()) {
364 		default:
365 		case EOF:
366 		case 'a':
367 			i.type = i.NOTHING;
368 			break;
369 		case 's':
370 			i.type = i.SPECIAL;
371 			goto atime;
372 		case 'd':
373 			i.type = i.DIRECTORY;
374 			goto atime;
375 		case 'f':
376 			i.type = i.FILE;
377 			goto cksum;
378 		cksum:
379 		{
380 #if defined(__WATCOMC__)
381 			s.setf(s.skipws);
382 			s.ipfx(0);
383 #else
384 			s >> std::skipws;
385 			std::istream::sentry ipfx(s, /*noskipws=*/false);
386 #endif
387 			for ( std::size_t j(0);j < sizeof i.hash/sizeof *i.hash; ++j) {
388 				char two[2];
389 				s.read(two, 2);
390 				i.hash[j] = static_cast<unsigned char>(x2d(two[0]) << 4) + static_cast<unsigned char>(x2d(two[1]));
391 			}
392 #if defined(__WATCOMC__)
393 			s.isfx();
394 			s.unsetf(s.skipws);
395 #else
396 			s >> std::noskipws;
397 #endif
398 		}
399 		atime:
400 #if defined(__WATCOMC__)
401 			s.setf(s.skipws);
402 #else
403 			s >> std::skipws;
404 #endif
405 			s >> std::hex >> i.last_written >> std::dec;
406 #if defined(__WATCOMC__)
407 			s.unsetf(s.skipws);
408 #else
409 			s >> std::noskipws;
410 #endif
411 			break;
412 		// FIXME: Delete this special case once we switch to the new .redo database filenames.
413 		case '\n':
414 		case '\t':
415 		case '\f':
416 		case '\b':
417 		case '\v':
418 		case '\r':
419 		case ' ':
420 		{
421 			char md5sum[128/8];
422 			i.type = i.FILE;
423 #if defined(__WATCOMC__)
424 			s.setf(s.skipws);
425 #else
426 			s >> std::skipws;
427 #endif
428 			s >> name >> std::hex >> i.last_written >> std::dec;
429 			for (std::size_t j(0); j < sizeof md5sum/sizeof *md5sum; ++j) {
430 				char two[2];
431 				s.read(two, 2);
432 				md5sum[j] = static_cast<char>(x2d(two[0]) << 4) + static_cast<char>(x2d(two[1]));
433 			}
434 #if defined(__WATCOMC__)
435 			s.unsetf(s.skipws);
436 #else
437 			s >> std::noskipws;
438 #endif
439 			return;
440 		}
441 	}
442 	while (!s.eof() && std::isspace(s.peek()))
443 		s.get();
444 	char namebuf[PATH_MAX];
445 	s.getline(namebuf, sizeof namebuf);
446 	name = namebuf;
447 }
448 
449 static inline
450 void
write_db_line(std::ostream & s,const Information & info,const char * name)451 write_db_line (
452 	std::ostream & s,
453 	const Information & info,
454 	const char * name
455 ) {
456 	switch (info.type) {
457 		case Information::NOTHING:	s.put('a') << name << '\n'; break;
458 		case Information::SPECIAL:	s.put('s') << std::hex << info.last_written << ' ' << std::dec << name << '\n'; break;
459 		case Information::DIRECTORY:	s.put('d') << std::hex << info.last_written << ' ' << std::dec << name << '\n'; break;
460 		case Information::FILE:
461 			s.put('f') << std::hex << std::setfill('0');
462 			for ( std::size_t j(0);j < (sizeof info.hash/sizeof *info.hash); ++j)
463 				s << std::setw(2) << static_cast<unsigned int>(info.hash[j]);
464 			s << ' ' << info.last_written << ' ' << std::setfill(' ') << std::dec << name << '\n';
465 			break;
466 	}
467 }
468 
469 static inline
470 void
puthash(std::ostream & s,const Information & info)471 puthash (
472 	std::ostream & s,
473 	const Information & info
474 ) {
475 	s << std::hex << std::setfill('0');
476 	for ( std::size_t j(0);j < sizeof info.hash/sizeof *info.hash; ++j)
477 		s << std::setw(2) << static_cast<unsigned int>(info.hash[j]);
478 	s << std::setfill(' ') << std::dec;
479 }
480 
481 /* Redo internals ***********************************************************
482 // **************************************************************************
483 */
484 
485 static inline
486 bool
record_prerequisites(const char * prog,const std::vector<const char * > & filev)487 record_prerequisites (
488 	const char * prog,
489 	const std::vector<const char *> & filev
490 ) {
491 	if (-1 == redoparent_fd) {
492 		msg(prog, "ERROR") << "Not invoked within a .do script.\n";
493 		return false;
494 	}
495 	if (debug) {
496 		msg(prog, "INFO") << "RECORD-PREREQUISITES: >";
497 		for ( std::vector<const char *>::const_iterator i = filev.begin(); i != filev.end(); ++i ) {
498 			const char * arg(*i);
499 			std::clog << ' ' << arg;
500 		}
501 		std::clog << '<' << std::endl;
502 	}
503 
504 	std::ostringstream tmp_db;
505 	for ( std::vector<const char *>::const_iterator i = filev.begin(); i != filev.end(); ++i ) {
506 		const char * arg(*i);
507 		const Information & info(get_file_info(arg, nullptr));
508 		write_db_line(tmp_db, info, arg);
509 		if (tmp_db.fail()) {
510 			int error = errno;
511 			msg(prog, "ERROR") << std::strerror(error) << "\n";
512 			return false;
513 		}
514 	}
515 	const std::string & s(tmp_db.str());
516 	if (0 > write(redoparent_fd, s.c_str(), s.length())) {
517 		int error = errno;
518 		msg(prog, "ERROR") << std::strerror(error) << "\n";
519 		return false;
520 	}
521 
522 	return true;
523 }
524 
525 static inline bool redo (bool, const char * prog, unsigned meta_depth, const std::vector<const char *> & filev);
526 
527 static
528 bool
redo_ifcreate(const char * prog,const std::vector<const char * > & filev)529 redo_ifcreate (
530 	const char * prog,
531 	const std::vector<const char *> & filev
532 ) {
533 	bool status(true);
534 	for ( std::vector<const char *>::const_iterator i = filev.begin(); i != filev.end(); ++i ) {
535 		const char * arg(*i);
536 		if (0 <= access(arg, F_OK)) {
537 			msg(prog, "ERROR") << arg << ": File/directory exists.\n";
538 			status = false;
539 			continue;
540 		}
541 	}
542 	if (!status) return status;
543 
544 	return record_prerequisites (prog, filev);
545 }
546 
547 static inline
548 bool
redo_ifcreate_1(const char * prog,const char * file)549 redo_ifcreate_1 (
550 	const char * prog,
551 	const char * file
552 ) {
553 	std::vector<const char *> filev;
554 	filev.push_back(file);
555 	return redo_ifcreate(prog, filev);
556 }
557 
558 static inline
559 bool
redo_ifchange(const char * prog,unsigned meta_depth,const std::vector<const char * > & filev)560 redo_ifchange (
561 	const char * prog,
562 	unsigned meta_depth,
563 	const std::vector<const char *> & filev
564 ) {
565 	return redo(false, prog, meta_depth, filev) && record_prerequisites(prog, filev);
566 }
567 
568 static inline
569 bool
redo_ifchange_1(const char * prog,unsigned meta_depth,const char * file)570 redo_ifchange_1 (
571 	const char * prog,
572 	unsigned meta_depth,
573 	const char * file
574 ) {
575 	std::vector<const char *> filev;
576 	filev.push_back(file);
577 	return redo_ifchange(prog, meta_depth, filev);
578 }
579 
580 class RedoParentFDStack {
581 public:
RedoParentFDStack(int new_fd)582 	RedoParentFDStack ( int new_fd ) : old_fd(redoparent_fd) { redoparent_fd = new_fd; }
~RedoParentFDStack()583 	~RedoParentFDStack() { redoparent_fd = old_fd; }
584 protected:
585 	int old_fd;
586 };
587 
588 static inline
589 bool
find_do_file(const char * prog,unsigned meta_depth,std::string dir,const char * const b,std::string & dofile_name,std::string & base,std::string & ext)590 find_do_file (
591 	const char * prog,
592 	unsigned meta_depth,
593 	std::string dir,
594 	const char * const b,
595 	std::string & dofile_name,
596 	std::string & base,
597 	std::string & ext
598 ) {
599 	const char * const be(extension(b));
600 	for (;;) {
601 		base = b;
602 		ext = std::string();
603 		dofile_name = dir + base + ".do";
604 		if (exists(dofile_name)) {
605 			redo_ifchange_1(prog, meta_depth + 1, dofile_name.c_str());
606 			return true;
607 		} else
608 			redo_ifcreate_1(prog, dofile_name.c_str());
609 
610 		for (const char * e(be); ; e = extension(e + 1)) {
611 			base = std::string(b, static_cast<std::size_t>(e - b));
612 			ext = e;
613 			dofile_name = dir + "default" + ext + ".do";
614 			if (exists(dofile_name)) {
615 				redo_ifchange_1(prog, meta_depth + 1, dofile_name.c_str());
616 				return true;
617 			} else
618 				redo_ifcreate_1(prog, dofile_name.c_str());
619 
620 			if (!*e) break;
621 		}
622 
623 		std::string::size_type len(dir.length());
624 		if (len < 2) return false;
625 		std::string::size_type slash(dir.find_last_of('/', len - 2));
626 		if (std::string::npos == slash)
627 			dir = std::string();
628 		else
629 			dir = dir.substr(0, slash + 1);
630 	}
631 }
632 
633 /* Jobs *********************************************************************
634 // **************************************************************************
635 */
636 
637 namespace {
638 
639 struct Job {
640 	pid_t pid;
641 	const char * arg;
642 	std::string do_file_name;
643 
644 	Job(const char *);
645 	bool run ( const char *, unsigned int );
646 	bool finish ( const char *, int, int );
647 
648 protected:
649 	std::string target;
650 	std::string tmp_target;
651 	std::string database_name;
652 	std::string tmp_database_name;
653 	std::string lock_database_name;
654 	FileDescriptorOwner lock_fd;
655 };
656 
657 typedef std::list<std::shared_ptr<Job> > JobList;
658 
659 }
660 
Job(const char * a)661 Job::Job(
662 	const char * a
663 ) :
664 	pid(-1),
665 	arg(a),
666 	do_file_name(),
667 	target(a),
668 	tmp_target(target + ".doing"),
669 	database_name(".redo/" + target + ".prereqs"),
670 	tmp_database_name(database_name + ".build"),
671 	lock_database_name(database_name + ".lock"),
672 	lock_fd(-1)
673 {
674 }
675 
676 inline
677 bool
run(const char * prog,unsigned meta_depth)678 Job::run (
679 	const char * prog,
680 	unsigned meta_depth
681 ) {
682 	pid = -1;
683 
684 	const char * b(basename_of(arg));
685 	if (b != arg)
686 		makepath(".redo/" + std::string(arg, static_cast<std::size_t>(b - 1 - arg)));
687 
688 	if (debug) {
689 		msg(prog, "INFO") << lock_database_name << ": Locking ...\n";
690 	}
691 	lock_fd.reset(open_lockfile_exclusive_or_wait_at(AT_FDCWD, lock_database_name.c_str()));
692 	if (0 > lock_fd.get()) {
693 		const int error(errno);
694 		msg(prog, "ERROR") << lock_database_name << ": " << std::strerror(error) << "\n";
695 		return false;
696 	}
697 	FileDescriptorOwner db_fd(open(tmp_database_name.c_str(), O_WRONLY|O_TRUNC|O_CREAT|O_NOCTTY, 0777));
698 	if (0 > db_fd.get()) {
699 		const int error(errno);
700 		msg(prog, "ERROR") << tmp_database_name << ": " << std::strerror(error) << "\n";
701 		return false;
702 	}
703 
704 	RedoParentFDStack saved_parent(db_fd.get());
705 	const std::string dir(arg, static_cast<std::size_t>(b - arg));
706 	std::string base, ext;
707 	if (!find_do_file(prog, meta_depth, dir, b, do_file_name, base, ext)) {
708 		msg(prog, "ERROR") << arg << ": Cannot find .do file to use.\n";
709 		return false;
710 	}
711 	const std::string fullbase(dir + base);
712 	std::remove(tmp_target.c_str());
713 
714 	const char * argv[9] = {
715 		do_file_name.c_str(),
716 		fullbase.c_str(),
717 		ext.c_str(),
718 		tmp_target.c_str(),
719 		nullptr
720 	};
721 	std::vector<const char *> envv;
722 	for (char **e(environ); *e; ++e)
723 		envv.push_back(*e);
724 	std::ostringstream redoflags;
725 	redoflags << "REDOFLAGS=";
726 	if (keep_going) redoflags << " --keep-going";
727 	if (debug) redoflags << " --debug";
728 	if (silent) redoflags << " --silent";
729 	if (verbose) redoflags << " --verbose";
730 	if (-1 != db_fd.get()) redoflags << " --redoparent-fd=" << db_fd.get();
731 	if (-1 != jobserver::fds[0]) {
732 		redoflags << " --jobserver-fds=" << jobserver::fds[0];
733 		if (-1 != jobserver::fds[1])
734 			redoflags << "," << jobserver::fds[1];
735 	}
736 	const std::string & redoflags_str(redoflags.str());
737 	envv.push_back(redoflags_str.c_str());
738 	envv.push_back(nullptr);
739 
740 	if (verbose)
741 		msg(prog, "INFO") << "spawn: " << do_file_name << " " << fullbase << " " << ext << " " << tmp_target << "\n" << std::flush;
742 	pid = spawn(do_file_name.c_str(), argv, const_cast<const char **>(&envv.front()));
743 
744 	return true;
745 }
746 
747 inline
748 bool
finish(const char * prog,int exit_status,int exit_code)749 Job::finish (
750 	const char * prog,
751 	int exit_status,
752 	int exit_code
753 ) {
754 	pid = -1;
755 	if (WAIT_STATUS_EXITED != exit_status || 0 < exit_code) {
756 		msg(prog, "ERROR") << target << ": Not done.\n";
757 		rmrf(tmp_target.c_str());
758 		lock_fd.reset(-1);
759 		return false;
760 	}
761 	struct stat stbuf;
762 	if (0 > lstat(tmp_target.c_str(), &stbuf)) {
763 		std::ofstream tmp(tmp_target.c_str(), std::ios::app);
764 	}
765 	if ((0 <= lstat(target.c_str(), &stbuf)) && S_ISDIR(stbuf.st_mode)) {
766 		if (0 > rmrf(target.c_str())) {
767 			const int error(errno);
768 			msg(prog, "ERROR") << target << ": Unable to remove contents of target directory: " << std::strerror(error) << "\n";
769 			rmrf(tmp_target.c_str());
770 			lock_fd.reset(-1);
771 			return false;
772 		}
773 	}
774 	delete_file_info(target);
775 	if (0 > std::rename(tmp_database_name.c_str(), database_name.c_str())) {
776 		const int error(errno);
777 		msg(prog, "ERROR") << tmp_database_name << ": Unable to rename database file: " << std::strerror(error) << "\n";
778 		rmrf(tmp_target.c_str());
779 		lock_fd.reset(-1);
780 		return false;
781 	}
782 	if (0 > std::rename(tmp_target.c_str(), target.c_str())) {
783 		const int error(errno);
784 		msg(prog, "ERROR") << target << ": Unable to rename target file: " << std::strerror(error) << "\n";
785 		rmrf(tmp_target.c_str());
786 		lock_fd.reset(-1);
787 		return false;
788 	}
789 	if (!silent) {
790 		msg(prog, "INFO") << target << ": Redone.\n" << std::flush;
791 	}
792 	lock_fd.reset(-1);
793 	return true;
794 }
795 
796 static inline
797 bool
run(const char * prog,unsigned meta_depth,JobList & jobs)798 run (
799 	const char * prog,
800 	unsigned meta_depth,
801 	JobList & jobs
802 ) {
803 	bool status = true;
804 	JobList::iterator begin(jobs.begin()), next_to_run(begin), next_to_await(begin), end(jobs.end());
805 	while (next_to_await != end) {
806 		if (debug) {
807 			if (next_to_run != end)
808 				msg(prog, "INFO") << "Jobs still available to start.\n";
809 			if (next_to_await != next_to_run)
810 				msg(prog, "INFO") << "Jobs still available to await.\n";
811 		}
812 		while ((next_to_run != end) && jobserver::try_procure_slot(prog)) {
813 			Job * const job(next_to_run->get());
814 			if (!job->run(prog, meta_depth)) {
815 				status = false;
816 				jobserver::vacate_slot(prog);
817 			} else if (-1 == job->pid) {
818 				const int error(errno);
819 				msg(prog, "ERROR") << job->do_file_name << ": " << std::strerror(error) << "\n";
820 				status = false;
821 				jobserver::vacate_slot(prog);
822 			}
823 			++next_to_run;
824 		}
825 		while ((next_to_await != next_to_run) && (-1 == next_to_await->get()->pid)) {
826 			++next_to_await;
827 		}
828 		if (next_to_await != next_to_run) {
829 			int exit_status, exit_code;
830 			pid_t pid;
831 			const int rc(wait_blocking_for_anychild_exit(pid, exit_status, exit_code));
832 			if (0 >= rc) {
833 				const int error(errno);
834 				msg(prog, "ERROR") << std::strerror(error) << "\n";
835 				status = false;
836 				continue;
837 			}
838 			if (debug)
839 				msg(prog, "INFO") << "Reaped child process ID " << pid << ".\n";
840 			bool found = false;
841 			for ( JobList::iterator i(next_to_await); i != next_to_run; ++i ) {
842 				Job * const job(i->get());
843 				if (pid == job->pid) {
844 					if (!job->finish(prog, exit_status, exit_code))
845 						status = false;
846 					jobserver::vacate_slot(prog);
847 					found = true;
848 					break;
849 				}
850 			}
851 			if (!found) {
852 				msg(prog, "ERROR") << "Unknown child process ID " << pid << ".\n";
853 			}
854 		}
855 	}
856 	return status;
857 }
858 
859 /* Redo internals ***********************************************************
860 // **************************************************************************
861 */
862 
863 static inline
864 bool
satisfies_existence(const char * prog,const std::string & target_name)865 satisfies_existence (
866 	const char * prog,
867 	const std::string & target_name
868 ) {
869 	if (!exists(target_name)) {
870 		if (verbose)
871 			msg(prog, "INFO") << target_name << " needs rebuilding because it does not exist.\n";
872 		return false;
873 	}
874 	return true;
875 }
876 
877 static inline
878 bool
satisfies_prerequisites(const char * prog,const std::string & target_name)879 satisfies_prerequisites (
880 	const char * prog,
881 	const std::string & target_name
882 ) {
883 	const std::string database_name(".redo/" + target_name + ".prereqs");
884 	const std::string lock_database_name(database_name + ".lock");
885 	const FileDescriptorOwner lock_fd(open_lockfile_shared_or_wait_at(AT_FDCWD, lock_database_name.c_str()));
886 	if (0 > lock_fd.get()) return false;
887 	std::ifstream file(database_name.c_str());
888 	if (file.fail()) return false;
889 	bool satisfaction(true);
890 	while (EOF != file.peek()) {
891 		Information db_info;
892 		std::string prereq_name;
893 		read_db_line(file, db_info, prereq_name);
894 		const Information & fs_info(get_file_info(prereq_name, &db_info));
895 		if (db_info.type != fs_info.type) {
896 			if (verbose) {
897 				msg(prog, "INFO") << target_name << " needs rebuilding because " << prereq_name;
898 				if (fs_info.NOTHING == fs_info.type)
899 					std::clog << " does not exist.\n";
900 				else
901 					std::clog << " has changed type.\n";
902 			}
903 			satisfaction = false;
904 			if (!keep_going) break;
905 		} else
906 		if (fs_info.NOTHING != fs_info.type
907 		&&  fs_info.last_written != db_info.last_written
908 		) {
909 			if (fs_info.SPECIAL == fs_info.type || fs_info.DIRECTORY == fs_info.type) {
910 				if (verbose) {
911 					char fs_buf[64], db_buf[64];
912 					struct std::tm fs_tm(*std::localtime(&fs_info.last_written));
913 					struct std::tm db_tm(*std::localtime(&db_info.last_written));
914 					std::strftime(fs_buf, sizeof fs_buf, "%F %T %z", &fs_tm);
915 					std::strftime(db_buf, sizeof db_buf, "%F %T %z", &db_tm);
916 					msg(prog, "INFO") << target_name << " needs rebuilding because " << prereq_name << " has changed timestamp from " << db_buf << " to " << fs_buf << ".\n";
917 				}
918 				satisfaction = false;
919 				if (!keep_going) break;
920 			} else
921 			if (0 != std::memcmp(fs_info.hash, db_info.hash, sizeof db_info.hash)) {
922 				if (verbose) {
923 					msg(prog, "INFO") << target_name << " needs rebuilding because " << prereq_name << " has changed hash value from ";
924 					puthash(std::clog, db_info);
925 					std::clog << " to ";
926 					puthash(std::clog, fs_info);
927 					std::clog << ".\n";
928 				}
929 				satisfaction = false;
930 				if (!keep_going) break;
931 			}
932 		}
933 	}
934 	return satisfaction;
935 }
936 
937 static inline
938 bool
is_sourcefile(const std::string & name)939 is_sourcefile(
940 	const std::string & name
941 ) {
942 	if (!exists(name)) return false;
943 	if (exists(".redo/" + name + ".prereqs")) return false;
944 	if (exists(".redo/" + name + ".prereqsne")) return false;
945 	if (exists(".redo/" + name + ".prereqs.build")) return false;
946 	if (exists(".redo/" + name + ".prereqsne.build")) return false;
947 	return true;
948 }
949 
950 static inline
951 bool
recurse_prerequisites(const char * prog,unsigned meta_depth,const std::string & name)952 recurse_prerequisites (
953 	const char * prog,
954 	unsigned meta_depth,
955 	const std::string & name
956 ) {
957 	const std::string database_name(".redo/" + name + ".prereqs");
958 	const std::string lock_database_name(database_name + ".lock");
959 	const FileDescriptorOwner lock_fd(open_lockfile_shared_or_wait_at(AT_FDCWD, lock_database_name.c_str()));
960 	if (0 > lock_fd.get()) return false;
961 	std::ifstream file(database_name.c_str());
962 	if (file.fail()) return false;
963 	std::list<std::string> files;
964 	while (EOF != file.peek()) {
965 		Information info;
966 		std::string prereq_name;
967 		read_db_line(file, info, prereq_name);
968 		if (info.NOTHING != info.type && !is_sourcefile(prereq_name))
969 			files.push_back(prereq_name);
970 	}
971 	return files.empty() || redo(false, prog, meta_depth, convert(files));
972 }
973 
974 static
975 bool
redo(bool unconditional,const char * prog,unsigned meta_depth,const std::vector<const char * > & filev)976 redo (
977 	bool unconditional,
978 	const char * prog,
979 	unsigned meta_depth,
980 	const std::vector<const char *> & filev
981 ) {
982 	if (meta_depth >= MAX_META_DEPTH) return true;
983 	bool status(true);
984 	if (debug) {
985 		msg(prog, "INFO") << "REDO" << (unconditional ? "" : "-IFCHANGE-INTERNAL") << ": >";
986 		for ( std::vector<const char *>::const_iterator i = filev.begin(); i != filev.end(); ++i ) {
987 			const char * arg(*i);
988 			std::clog << ' ';
989 			if (is_sourcefile(arg)) std::clog << "(" << arg << ")"; else std::clog << arg;
990 		}
991 		std::clog << '<' << std::endl;
992 	}
993 
994 	JobList jobs;
995 
996 	for ( std::vector<const char *>::const_iterator i = filev.begin(); i != filev.end(); ++i ) {
997 		const char * arg(*i);
998 		if (is_root_or_ends_with_dot_or_dotdot(arg)) continue; // Treat as source files, because they always exist.
999 		if (is_sourcefile(arg)) continue;
1000 		if (!unconditional) {
1001 			if (satisfies_existence(prog, arg)) {
1002 				if (recurse_prerequisites(prog, meta_depth, arg)
1003 				&&  satisfies_existence(prog, arg)
1004 				&&  satisfies_prerequisites(prog, arg)
1005 				)
1006 					continue;
1007 			}
1008 		}
1009 
1010 		jobs.push_back(std::shared_ptr<Job>(new Job(arg)));
1011 	}
1012 	if (!run(prog, meta_depth, jobs))
1013 		status = false;
1014 	if (debug) {
1015 		msg(prog, "INFO") << "Redo status = " << status << ".\n" << std::flush;
1016 	}
1017 	return status;
1018 }
1019 
1020 /* Main functions ***********************************************************
1021 // **************************************************************************
1022 */
1023 
1024 static
1025 bool
parse_fds(const char * prog,const char * str,int fds[],size_t count)1026 parse_fds(
1027 	const char * prog,
1028 	const char * str,
1029 	int fds[],
1030 	size_t count
1031 ) {
1032 	char * s(const_cast<char *>(str));
1033 	for (size_t j(0); j < count; ++j) {
1034 		if (j > 0) {
1035 			if (!*s) {
1036 				fds[j] = fds[j - 1];
1037 				continue;
1038 			} else
1039 			if (',' != *s) {
1040 				msg(prog, "ERROR") << str << ": " << s << ": Missing comma.\n";
1041 				return false;
1042 			} else
1043 				++s;
1044 		}
1045 		const char * old(s);
1046 		long i(std::strtol(old, &s, 0));
1047 		if (old == s) {
1048 			msg(prog, "ERROR") << str << ": " << s << ": File descriptor number not supplied.\n";
1049 			return false;
1050 		}
1051 		if (i > INT_MAX) {
1052 			msg(prog, "ERROR") << str << ": " << s << ": File descriptor number too large.\n";
1053 			return false;
1054 		}
1055 		fds[j] = static_cast<int>(i);
1056 	}
1057 	if (*s) {
1058 		msg(prog, "ERROR") << str << ": " << s << ": Trailing junk after file descriptor(s).\n";
1059 		return false;
1060 	}
1061 	return true;
1062 }
1063 
1064 int
redo_main(const char * prog,int argc,const char * argv[])1065 redo_main ( const char * prog, int argc, const char * argv[] )
1066 {
1067         std::vector<const char *> filev;
1068 
1069 	try {
1070 		std::string jobserver_fds_string;
1071 		std::string redoparent_fd_string;
1072 		const char * jobserver_fds_c_str = nullptr;
1073 		const char * redoparent_fd_c_str = nullptr;
1074 		const char * directory = nullptr;
1075 		unsigned long max_jobs = 0;
1076 		popt::bool_definition silent_option('s', "silent", "Operate quietly.", silent);
1077 		popt::bool_definition quiet_option('\0', "quiet", "alias for --silent", silent);
1078 		popt::bool_definition keep_going_option('k', "keep-going", "Continue with the next target if a .do script fails.", keep_going);
1079 		popt::bool_definition debug_option('d', "debug", "Output debugging information.", debug);
1080 		popt::bool_definition verbose_option('\0', "verbose", "Display information about the database.", verbose);
1081 		popt::bool_definition print_option('p', "print", "alias for --verbose", verbose);
1082 		popt::unsigned_number_definition jobs_option('j', "jobs", "number", "Allow multiple jobs to run in parallel.", max_jobs, 0);
1083 		popt::string_definition directory_option('C', "directory", "directory", "Change to directory before doing anything.", directory);
1084 		popt::definition * top_table[] = {
1085 			&silent_option,
1086 			&quiet_option,
1087 			&debug_option,
1088 			&keep_going_option,
1089 			&verbose_option,
1090 			&print_option,
1091 			&jobs_option,
1092 			&directory_option
1093 		};
1094 		popt::top_table_definition main_option(sizeof top_table/sizeof *top_table, top_table, "Main options", "filename(s)");
1095 		catchall_definition ignore;
1096 		special_string_definition new_make_jobserver_option('J', "jobserver-auth", "fd-list", "Provide the file descriptor numbers of the jobserver pipe.", jobserver_fds_c_str);
1097 		special_string_definition old_make_jobserver_option('\0', "jobserver-fds", "fd-list", "Provide the file descriptor numbers of the jobserver pipe.", jobserver_fds_c_str);
1098 		popt::definition * make_env_table[] = {
1099 			&silent_option,
1100 			&quiet_option,
1101 			&debug_option,
1102 			&keep_going_option,
1103 			&verbose_option,
1104 			&print_option,
1105 			&jobs_option,
1106 			&new_make_jobserver_option,
1107 			&old_make_jobserver_option,
1108 			&ignore
1109 		};
1110 		popt::table_definition make_env_main_option(sizeof make_env_table/sizeof *make_env_table, make_env_table, "Main options (environment variable arguments)");
1111 		special_string_definition redo_jobserver_option('\0', "jobserver-fds", "fd-list", "Provide the file descriptor numbers of the jobserver pipe.", jobserver_fds_c_str);
1112 		special_string_definition redoparent_option('\0', "redoparent-fd", "fd", "Provide the file descriptor number of the redo database current parent file.", redoparent_fd_c_str);
1113 		popt::definition * redo_env_table[] = {
1114 			&silent_option,
1115 			&quiet_option,
1116 			&debug_option,
1117 			&keep_going_option,
1118 			&verbose_option,
1119 			&print_option,
1120 			&jobs_option,
1121 			&redo_jobserver_option,
1122 			&redoparent_option,
1123 		};
1124 		popt::table_definition redo_env_main_option(sizeof redo_env_table/sizeof *redo_env_table, redo_env_table, "Main options (environment variable arguments)");
1125 
1126 		static const char * vars[] = { "REDOFLAGS", "MAKEFLAGS", "MFLAGS" };
1127 		for ( unsigned i = 0 ; i < sizeof vars/sizeof *vars; ++i ) {
1128 			const char * var(vars[i]);
1129 			if (const char * s = std::getenv(var)) {
1130 				std::list<std::string> args(split(s, 1 == i));	 // Special bodge for MAKEFLAGS, which omits the "-" from the first option.
1131 				std::vector<const char *> env_argv(convert(args));
1132 				std::vector<const char *> env_filev;
1133 				popt::arg_processor<std::vector<const char *>::const_iterator> p(env_argv.begin(), env_argv.end(), prog, i ? make_env_main_option : redo_env_main_option, env_filev);
1134 				p.process(false /* allow intermingling of options and arguments */);
1135 				if (p.stopped())
1136 					msg(prog, "WARNING") << var << ": Ignoring halt of option processing.\n";
1137 				if (1 == i) {	// Special bodge for MAKEFLAGS, which BSD make puts macro definitions into.
1138 					for ( std::vector<const char *>::iterator j = env_filev.begin(); j != env_filev.end(); ) {
1139 						if (std::strchr(*j, '='))
1140 							j = env_filev.erase(j);
1141 						else
1142 							++j;
1143 					}
1144 				}
1145 				if (!env_filev.empty())
1146 					msg(prog, "WARNING") << var << ": " << env_filev.front() << ": Ignoring filename(s).\n";
1147 				if (jobserver_fds_c_str) { jobserver_fds_string = jobserver_fds_c_str; jobserver_fds_c_str = nullptr; }
1148 				if (redoparent_fd_c_str) { redoparent_fd_string = redoparent_fd_c_str; redoparent_fd_c_str = nullptr; }
1149 				break;
1150 			}
1151 		}
1152 
1153 		popt::arg_processor<const char **> p(argv + 1, argv + argc, prog, main_option, filev);
1154 		p.process(false /* allow intermingling of options and arguments */);
1155 		if (p.stopped()) return EXIT_SUCCESS;
1156 		if (directory) {
1157 			const int r(chdir(directory));
1158 			if (r < 0) {
1159 				const int error(errno);
1160 				msg(prog, "FATAL") << directory << ": " << std::strerror(error) << "\n";
1161 				return EXIT_FAILURE;
1162 			}
1163 		}
1164 		if (jobserver_fds_c_str) { jobserver_fds_string = jobserver_fds_c_str; jobserver_fds_c_str = nullptr; }
1165 		if (redoparent_fd_c_str) { redoparent_fd_string = redoparent_fd_c_str; redoparent_fd_c_str = nullptr; }
1166 
1167 		if (!jobserver_fds_string.empty()) {
1168 			if (!parse_fds(prog, jobserver_fds_string.c_str(), jobserver::fds, sizeof jobserver::fds/sizeof *jobserver::fds))
1169 				return EXIT_FAILURE;
1170 			if (jobs_option.is_set())
1171 				msg(prog, "WARNING") << "Ignoring jobs option when there's already a job server.\n";
1172 		} else {
1173 			if (jobs_option.is_set()) {
1174 				if (max_jobs < 1U) {
1175 					msg(prog, "FATAL") << "Invalid job limit.\n";
1176 					return EXIT_FAILURE;
1177 				}
1178 				if (0 > pipe(jobserver::fds)) {
1179 					const int error(errno);
1180 					msg(prog, "FATAL") << "pipe: " << std::strerror(error) << "\n";
1181 					return EXIT_FAILURE;
1182 				}
1183 				for (unsigned long m(max_jobs); m > 1U; --m)
1184 					jobserver::vacate_slot(prog);
1185 			}
1186 		}
1187 		if (!redoparent_fd_string.empty()) {
1188 			if (!parse_fds(prog, redoparent_fd_string.c_str(), &redoparent_fd, 1U))
1189 				return EXIT_FAILURE;
1190 		}
1191 	} catch (const popt::error & e) {
1192 		msg(prog, "FATAL") << e.arg << ": " << e.msg << "\n";
1193 		return EXIT_FAILURE;
1194 	}
1195 
1196 	if (filev.empty()) {
1197 		msg(prog, "FATAL") << "No filenames supplied.\n";
1198 		return EXIT_FAILURE;
1199 	}
1200 
1201 	const unsigned meta_depth(0U);
1202 
1203 	// Increment MAKELEVEL
1204 	char gnulevelbuf[sizeof "MAKELEVEL=" + 64] = "MAKELEVEL=";
1205 	char bsdlevelbuf[sizeof "__MKLVL__=" + 64] = "__MKLVL__=";
1206 	{
1207 		const char * level(std::getenv("MAKELEVEL"));
1208 		if (!level)
1209 			level = std::getenv("__MKLVL__");
1210 		if (level) {
1211 			makelevel = level;
1212 			unsigned long ul(std::strtoul(level, const_cast<char **>(&level), 0));
1213 			snprintf(gnulevelbuf + sizeof "MAKELEVEL=" - 1, 64, "%lu", ul + 1);
1214 			snprintf(bsdlevelbuf + sizeof "__MKLVL__=" - 1, 64, "%lu", ul + 1);
1215 		} else
1216 		{
1217 			snprintf(gnulevelbuf + sizeof "MAKELEVEL=" - 1, 64, "%lu", 1UL);
1218 			snprintf(bsdlevelbuf + sizeof "__MKLVL__=" - 1, 64, "%lu", 1UL);
1219 		}
1220 	}
1221 	putenv(gnulevelbuf);
1222 	putenv(bsdlevelbuf);
1223 
1224 	if (0 == std::strcmp(prog, "redo-ifcreate"))
1225 		return redo_ifcreate(prog, filev) ? EXIT_SUCCESS : EXIT_FAILURE;
1226 	else
1227 	if (0 == std::strcmp(prog, "redo-ifchange"))
1228 		return redo_ifchange(prog, meta_depth, filev) ? EXIT_SUCCESS : EXIT_FAILURE;
1229 	else
1230 	if (0 == std::strcmp(prog, "redo")) {
1231 		mkdir(".redo", 0777);
1232 		return redo(true, prog, meta_depth, filev) ? EXIT_SUCCESS : EXIT_FAILURE;
1233 	} else
1234 	{
1235 		msg(prog, "FATAL") << prog << ": Unknown function.\n";
1236 		return EXIT_FAILURE;
1237 	}
1238 }
1239 
1240 int
cubehash_main(const char * prog,int argc,const char * argv[])1241 cubehash_main ( const char * prog, int argc, const char * argv[] )
1242 {
1243         std::vector<const char *> filev;
1244 
1245 	try {
1246 		const char * directory = nullptr;
1247 		popt::string_definition directory_option('C', "directory", "directory", "Change to directory before doing anything.", directory);
1248 		popt::definition * top_table[] = {
1249 			&directory_option
1250 		};
1251 		popt::top_table_definition main_option(sizeof top_table/sizeof *top_table, top_table, "Main options", "filename(s)");
1252 		popt::arg_processor<const char **> p(argv + 1, argv + argc, prog, main_option, filev);
1253 		p.process(false /* allow intermingling of options and arguments */);
1254 		if (p.stopped()) return EXIT_SUCCESS;
1255 		if (directory) {
1256 			const int r(chdir(directory));
1257 			if (r < 0) {
1258 				const int error(errno);
1259 				msg(prog, "FATAL") << directory << ": " << std::strerror(error) << "\n";
1260 				return EXIT_FAILURE;
1261 			}
1262 		}
1263 	} catch (const popt::error & e) {
1264 		msg(prog, "FATAL") << e.arg << ": " << e.msg << "\n";
1265 		return EXIT_FAILURE;
1266 	}
1267 
1268 	if (filev.empty()) {
1269 		msg(prog, "FATAL") << "No filenames supplied.\n";
1270 		return EXIT_FAILURE;
1271 	}
1272 
1273 	for ( std::vector<const char *>::const_iterator i = filev.begin(); i != filev.end(); ++i ) {
1274 		const char * arg(*i);
1275 		const Information & info(get_file_info(arg, nullptr));
1276 		write_db_line(std::cout, info, arg);
1277 	}
1278 	return EXIT_SUCCESS;
1279 }
1280 
1281 int
main(int argc,const char * argv[])1282 main ( int argc, const char * argv[] )
1283 {
1284 	const char * prog(basename_of(argv[0]));
1285 
1286 	if (0 == std::strcmp(prog, "cubehash"))
1287 		return cubehash_main(prog, argc, argv);
1288 	else
1289 		return redo_main(prog, argc, argv);
1290 }
1291