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