1 /*
2 * CDDL HEADER START
3 *
4 * This file and its contents are supplied under the terms of the
5 * Common Development and Distribution License ("CDDL"), version 1.0.
6 * You may use this file only in accordance with the terms of version
7 * 1.0 of the CDDL.
8 *
9 * A full copy of the text of the CDDL should have accompanied this
10 * source. A copy of the CDDL is also available via the Internet at
11 * http://www.opensource.org/licenses/cddl1.txt
12 * See the License for the specific language governing permissions
13 * and limitations under the License.
14 *
15 * When distributing Covered Code, include this CDDL HEADER in each
16 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
17 * If applicable, add the following below this CDDL HEADER, with the
18 * fields enclosed by brackets "[]" replaced with your own identifying
19 * information: Portions Copyright [yyyy] [name of copyright owner]
20 *
21 * CDDL HEADER END
22 */
23 /*
24 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
26 */
27 /*
28 * @(#)state.cc 1.27 06/12/12
29 */
30
31 #pragma ident "@(#)state.cc 1.27 06/12/12"
32
33 /*
34 * Copyright 2017-2018 J. Schilling
35 *
36 * @(#)state.cc 1.7 21/08/15 2017-2018 J. Schilling
37 */
38 #include <schily/mconfig.h>
39 #ifndef lint
40 static UConst char sccsid[] =
41 "@(#)state.cc 1.7 21/08/15 2017-2018 J. Schilling";
42 #endif
43
44 /*
45 * state.c
46 *
47 * This file contains the routines that write the .make.state file
48 */
49
50 /*
51 * Included files
52 */
53 #include <mk/defs.h>
54 #include <mksh/misc.h> /* errmsg() */
55 #include <setjmp.h> /* setjmp() */
56 #include <locale.h> /* MB_CUR_MAX */
57
58 /*
59 * Defined macros
60 */
61 #define LONGJUMP_VALUE 17
62 #define XFWRITE(string, length, fd) {if (fwrite(string, 1, length, fd) == 0) \
63 longjmp(long_jump, LONGJUMP_VALUE);}
64 #define XPUTC(ch, fd) { \
65 if (putc((int) ch, fd) == EOF) \
66 longjmp(long_jump, LONGJUMP_VALUE); \
67 }
68 #define XFPUTS(string, fd) fputs(string, fd)
69
70 /*
71 * typedefs & structs
72 */
73
74 /*
75 * Static variables
76 */
77
78 /*
79 * File table of contents
80 */
escape_target_name(Name np)81 static char * escape_target_name(Name np)
82 {
83 if(np->dollar) {
84 int len = strlen(np->string_mb);
85 char * buff = (char*)malloc(2 * len);
86 int pos = 0;
87 wchar_t wc;
88 int pp = 0;
89 while(pos < len) {
90 int n = mbtowc(&wc, np->string_mb + pos, MB_CUR_MAX);
91 if(n < 0) { // error - this shouldn't happen
92 (void)free(buff);
93 return strdup(np->string_mb);
94 }
95 if(wc == dollar_char) {
96 buff[pp] = '\\'; pp++;
97 buff[pp] = '$'; pp++;
98 } else {
99 for(int j=0;j<n;j++) {
100 buff[pp] = np->string_mb[pos+j]; pp++;
101 }
102 }
103 pos += n;
104 }
105 buff[pp] = '\0';
106 return buff;
107 } else {
108 return strdup(np->string_mb);
109 }
110 }
111
112 static void print_auto_depes(register Dependency dependency, register FILE *fd, register Boolean built_this_run, register int *line_length, register char *target_name, jmp_buf long_jump);
113
114 /*
115 * write_state_file(report_recursive, exiting)
116 *
117 * Write a new version of .make.state
118 *
119 * Parameters:
120 * report_recursive Should only be done at end of run
121 * exiting true if called from the exit handler
122 *
123 * Global variables used:
124 * built_last_make_run The Name ".BUILT_LAST_MAKE_RUN", written
125 * command_changed If no command changed we do not need to write
126 * current_make_version The Name "<current version>", written
127 * do_not_exec_rule If -n is on we do not write statefile
128 * hashtab The hashtable that contains all names
129 * keep_state If .KEEP_STATE is no on we do not write file
130 * make_state The Name ".make.state", used for opening file
131 * make_version The Name ".MAKE_VERSION", written
132 * recursive_name The Name ".RECURSIVE", written
133 * rewrite_statefile Indicates that something changed
134 */
135
136 void
137 #ifdef NSE
write_state_file(int report_recursive,Boolean exiting)138 write_state_file(int report_recursive, Boolean exiting)
139 #else
140 write_state_file(int, Boolean exiting)
141 #endif
142 {
143 register FILE *fd;
144 int lock_err;
145 char buffer[MAXPATHLEN];
146 char make_state_tempfile[MAXPATHLEN];
147 jmp_buf long_jump;
148 register int attempts = 0;
149 Name_set::iterator np, e;
150 register Property lines;
151 register int m;
152 Dependency dependency;
153 register Boolean name_printed;
154 Boolean built_this_run = false;
155 char *target_name;
156 int line_length;
157 register Cmd_line cp;
158
159
160 if (!rewrite_statefile ||
161 !command_changed ||
162 !keep_state ||
163 do_not_exec_rule ||
164 (report_dependencies_level > 0)) {
165 return;
166 }
167 /* Lock the file for writing. */
168 make_state_lockfile = getmem(strlen(make_state->string_mb) + strlen(NOCATGETS(".lock")) + 1);
169 (void) sprintf(make_state_lockfile,
170 NOCATGETS("%s.lock"),
171 make_state->string_mb);
172 if ((lock_err = file_lock(make_state->string_mb,
173 make_state_lockfile,
174 (int *) &make_state_locked, 0)) != 0) {
175 retmem_mb(make_state_lockfile);
176 make_state_lockfile = NULL;
177
178 /*
179 * We need to make sure that we are not being
180 * called by the exit handler so we don't call
181 * it again.
182 */
183
184 if (exiting) {
185 (void) sprintf(buffer, NOCATGETS("%s/.make.state.%d.XXXXXX"), tmpdir, getpid());
186 report_pwd = true;
187 warning(gettext("Writing to %s"), buffer);
188 int fdes = mkstemp(buffer);
189 if ((fdes < 0) || (fd = fdopen(fdes, "w")) == NULL) {
190 fprintf(stderr,
191 gettext("Could not open statefile `%s': %s"),
192 buffer,
193 errmsg(errno));
194 return;
195 }
196 } else {
197 report_pwd = true;
198 fatal(gettext("Can't lock .make.state"));
199 }
200 }
201
202 (void) sprintf(make_state_tempfile,
203 NOCATGETS("%s.tmp"),
204 make_state->string_mb);
205 /* Delete old temporary statefile (in case it exists) */
206 (void) unlink(make_state_tempfile);
207 if ((fd = fopen(make_state_tempfile, "w")) == NULL) {
208 lock_err = errno; /* Save it! unlink() can change errno */
209 (void) unlink(make_state_lockfile);
210 retmem_mb(make_state_lockfile);
211 make_state_lockfile = NULL;
212 make_state_locked = false;
213 fatal(gettext("Could not open temporary statefile `%s': %s"),
214 make_state_tempfile,
215 errmsg(lock_err));
216 }
217 #ifdef NSE
218 if (nse) {
219 (void) fchmod(fileno(fd), 0666);
220 }
221 #endif
222 /*
223 * Set a trap for failed writes. If a write fails, the routine
224 * will try saving the .make.state file under another name in /tmp.
225 */
226 if (setjmp(long_jump)) {
227 (void) fclose(fd);
228 if (attempts++ > 5) {
229 if ((make_state_lockfile != NULL) &&
230 make_state_locked) {
231 (void) unlink(make_state_lockfile);
232 retmem_mb(make_state_lockfile);
233 make_state_lockfile = NULL;
234 make_state_locked = false;
235 }
236 fatal(gettext("Giving up on writing statefile"));
237 }
238 sleep(10);
239 (void) sprintf(buffer, NOCATGETS("%s/.make.state.%d.XXXXXX"), tmpdir, getpid());
240 int fdes = mkstemp(buffer);
241 if ((fdes < 0) || (fd = fdopen(fdes, "w")) == NULL) {
242 fatal(gettext("Could not open statefile `%s': %s"),
243 buffer,
244 errmsg(errno));
245 }
246 warning(gettext("Initial write of statefile failed. Trying again on %s"),
247 buffer);
248 }
249
250 /* Write the version stamp. */
251 XFWRITE(make_version->string_mb,
252 strlen(make_version->string_mb),
253 fd);
254 XPUTC(colon_char, fd);
255 XPUTC(tab_char, fd);
256 XFWRITE(current_make_version->string_mb,
257 strlen(current_make_version->string_mb),
258 fd);
259 XPUTC(newline_char, fd);
260
261 /*
262 * Go through all the targets, dump their dependencies and
263 * command used.
264 */
265 for (np = hashtab.begin(), e = hashtab.end(); np != e; np++) {
266 /*
267 * If the target has no command used nor dependencies,
268 * we can go to the next one.
269 */
270 if ((lines = get_prop(np->prop, line_prop)) == NULL) {
271 continue;
272 }
273 /* If this target is a special target, don't print. */
274 if (np->special_reader != no_special) {
275 continue;
276 }
277 /*
278 * Find out if any of the targets dependencies should
279 * be written to .make.state.
280 */
281 for (m = 0, dependency = lines->body.line.dependencies;
282 dependency != NULL;
283 dependency = dependency->next) {
284 if ((m = !dependency->stale) != 0
285 && (dependency->name != force)
286 #ifndef PRINT_EXPLICIT_DEPEN
287 && dependency->automatic
288 #endif
289 ) {
290 break;
291 }
292 }
293 /* Only print if dependencies listed. */
294 if (m || (lines->body.line.command_used != NULL)) {
295 name_printed = false;
296 /*
297 * If this target was built during this make run,
298 * we mark it.
299 */
300 built_this_run = false;
301 if (np->has_built) {
302 built_this_run = true;
303 XFWRITE(built_last_make_run->string_mb,
304 strlen(built_last_make_run->string_mb),
305 fd);
306 XPUTC(colon_char, fd);
307 XPUTC(newline_char, fd);
308 }
309 /* If the target has dependencies, we dump them. */
310 target_name = escape_target_name(np);
311 if (np->has_long_member_name) {
312 target_name =
313 get_prop(np->prop, long_member_name_prop)
314 ->body.long_member_name.member_name->
315 string_mb;
316 }
317 if (m) {
318 XFPUTS(target_name, fd);
319 XPUTC(colon_char, fd);
320 XFPUTS("\t", fd);
321 name_printed = true;
322 line_length = 0;
323 for (dependency =
324 lines->body.line.dependencies;
325 dependency != NULL;
326 dependency = dependency->next) {
327 print_auto_depes(dependency,
328 fd,
329 built_this_run,
330 &line_length,
331 target_name,
332 long_jump);
333 }
334 XFPUTS("\n", fd);
335 }
336 /* If there is a command used, we dump it. */
337 if (lines->body.line.command_used != NULL) {
338 /*
339 * Only write the target name if it
340 * wasn't done for the dependencies.
341 */
342 if (!name_printed) {
343 XFPUTS(target_name, fd);
344 XPUTC(colon_char, fd);
345 XPUTC(newline_char, fd);
346 }
347 /*
348 * Write the command lines.
349 * Prefix each textual line with a tab.
350 */
351 for (cp = lines->body.line.command_used;
352 cp != NULL;
353 cp = cp->next) {
354 char *csp;
355 int n;
356
357 XPUTC(tab_char, fd);
358 if (cp->command_line != NULL) {
359 for (csp = cp->
360 command_line->
361 string_mb,
362 n = strlen(cp->
363 command_line->
364 string_mb);
365 n > 0;
366 n--, csp++) {
367 XPUTC(*csp, fd);
368 if (*csp ==
369 (int) newline_char) {
370 XPUTC(tab_char,
371 fd);
372 }
373 }
374 }
375 XPUTC(newline_char, fd);
376 }
377 }
378 (void)free(target_name);
379 }
380 }
381 if (fclose(fd) == EOF) {
382 longjmp(long_jump, LONGJUMP_VALUE);
383 }
384 if (attempts == 0) {
385 if (unlink(make_state->string_mb) != 0 && errno != ENOENT) {
386 lock_err = errno; /* Save it! unlink() can change errno */
387 /* Delete temporary statefile */
388 (void) unlink(make_state_tempfile);
389 (void) unlink(make_state_lockfile);
390 retmem_mb(make_state_lockfile);
391 make_state_lockfile = NULL;
392 make_state_locked = false;
393 fatal(gettext("Could not delete old statefile `%s': %s"),
394 make_state->string_mb,
395 errmsg(lock_err));
396 }
397 if (rename(make_state_tempfile, make_state->string_mb) != 0) {
398 lock_err = errno; /* Save it! unlink() can change errno */
399 /* Delete temporary statefile */
400 (void) unlink(make_state_tempfile);
401 (void) unlink(make_state_lockfile);
402 retmem_mb(make_state_lockfile);
403 make_state_lockfile = NULL;
404 make_state_locked = false;
405 fatal(gettext("Could not rename `%s' to `%s': %s"),
406 make_state_tempfile,
407 make_state->string_mb,
408 errmsg(lock_err));
409 }
410 }
411 if ((make_state_lockfile != NULL) && make_state_locked) {
412 (void) unlink(make_state_lockfile);
413 retmem_mb(make_state_lockfile);
414 make_state_lockfile = NULL;
415 make_state_locked = false;
416 }
417 #ifdef NSE
418 if (report_recursive) {
419 report_recursive_done();
420 }
421 #endif
422 }
423
424 /*
425 * print_auto_depes(dependency, fd, built_this_run,
426 * line_length, target_name, long_jump)
427 *
428 * Will print a dependency list for automatic entries.
429 *
430 * Parameters:
431 * dependency The dependency to print
432 * fd The file to print it to
433 * built_this_run If on we prefix each line with .BUILT_THIS...
434 * line_length Pointer to line length var that we update
435 * target_name We need this when we restart line
436 * long_jump setjmp/longjmp buffer used for IO error action
437 *
438 * Global variables used:
439 * built_last_make_run The Name ".BUILT_LAST_MAKE_RUN", written
440 * force The Name " FORCE", compared against
441 */
442 static void
print_auto_depes(register Dependency dependency,register FILE * fd,register Boolean built_this_run,register int * line_length,register char * target_name,jmp_buf long_jump)443 print_auto_depes(register Dependency dependency, register FILE *fd, register Boolean built_this_run, register int *line_length, register char *target_name, jmp_buf long_jump)
444 {
445 if (!dependency->automatic ||
446 dependency->stale ||
447 (dependency->name == force)) {
448 return;
449 }
450 XFWRITE(dependency->name->string_mb,
451 strlen(dependency->name->string_mb),
452 fd);
453 /*
454 * Check if the dependency line is too long.
455 * If so, break it and start a new one.
456 */
457 if ((*line_length += (int) strlen(dependency->name->string_mb) + 1) > 450) {
458 *line_length = 0;
459 XPUTC(newline_char, fd);
460 if (built_this_run) {
461 XFPUTS(built_last_make_run->string_mb, fd);
462 XPUTC(colon_char, fd);
463 XPUTC(newline_char, fd);
464 }
465 XFPUTS(target_name, fd);
466 XPUTC(colon_char, fd);
467 XPUTC(tab_char, fd);
468 } else {
469 XFPUTS(" ", fd);
470 }
471 return;
472 }
473
474
475