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