428
+ − 1 /* Modified version of unexec for convex machines.
+ − 2 Copyright (C) 1985, 1986, 1988 Free Software Foundation, Inc.
+ − 3
+ − 4 This file is part of XEmacs.
+ − 5
+ − 6 XEmacs is free software; you can redistribute it and/or modify it
+ − 7 under the terms of the GNU General Public License as published by the
+ − 8 Free Software Foundation; either version 2, or (at your option) any
+ − 9 later version.
+ − 10
+ − 11 XEmacs is distributed in the hope that it will be useful, but WITHOUT
+ − 12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ − 13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ − 14 for more details.
+ − 15
+ − 16 You should have received a copy of the GNU General Public License
+ − 17 along with XEmacs; see the file COPYING. If not, write to
+ − 18 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ − 19 Boston, MA 02111-1307, USA. */
+ − 20
+ − 21 /* Synched up with: FSF 19.31. */
+ − 22
+ − 23
+ − 24 /* modified for C-1 arch by jthomp@convex 871103 */
+ − 25 /* Corrected to support convex SOFF object file formats and thread specific
+ − 26 * regions. streepy@convex 890302
+ − 27 */
+ − 28
+ − 29 /*
+ − 30 * unexec.c - Convert a running program into an a.out file.
+ − 31 *
+ − 32 * Author: Spencer W. Thomas
+ − 33 * Computer Science Dept.
+ − 34 * University of Utah
+ − 35 * Date: Tue Mar 2 1982
+ − 36 * Modified heavily since then.
+ − 37 *
+ − 38 * Synopsis:
+ − 39 * unexec (new_name, a_name, data_start, bss_start, entry_address)
+ − 40 * char *new_name, *a_name;
+ − 41 * unsigned data_start, bss_start, entry_address;
+ − 42 *
+ − 43 * Takes a snapshot of the program and makes an a.out format file in the
+ − 44 * file named by the string argument new_name.
+ − 45 * If a_name is non-NULL, the symbol table will be taken from the given file.
+ − 46 * On some machines, an existing a_name file is required.
+ − 47 *
+ − 48 * The boundaries within the a.out file may be adjusted with the data_start
+ − 49 * and bss_start arguments. Either or both may be given as 0 for defaults.
+ − 50 *
+ − 51 * Data_start gives the boundary between the text segment and the data
+ − 52 * segment of the program. The text segment can contain shared, read-only
+ − 53 * program code and literal data, while the data segment is always unshared
+ − 54 * and unprotected. Data_start gives the lowest unprotected address.
+ − 55 * The value you specify may be rounded down to a suitable boundary
+ − 56 * as required by the machine you are using.
+ − 57 *
+ − 58 * Specifying zero for data_start means the boundary between text and data
+ − 59 * should not be the same as when the program was loaded.
+ − 60 * If NO_REMAP is defined, the argument data_start is ignored and the
+ − 61 * segment boundaries are never changed.
+ − 62 *
+ − 63 * Bss_start indicates how much of the data segment is to be saved in the
+ − 64 * a.out file and restored when the program is executed. It gives the lowest
+ − 65 * unsaved address, and is rounded up to a page boundary. The default when 0
+ − 66 * is given assumes that the entire data segment is to be stored, including
+ − 67 * the previous data and bss as well as any additional storage allocated with
+ − 68 * break (2).
+ − 69 *
+ − 70 * The new file is set up to start at entry_address.
+ − 71 *
+ − 72 * If you make improvements I'd like to get them too.
+ − 73 * harpo!utah-cs!thomas, thomas@Utah-20
+ − 74 *
+ − 75 */
+ − 76
+ − 77 /* There are several compilation parameters affecting unexec:
+ − 78
+ − 79 * COFF
+ − 80
+ − 81 Define this if your system uses COFF for executables.
+ − 82 Otherwise we assume you use Berkeley format.
+ − 83
+ − 84 * NO_REMAP
+ − 85
+ − 86 Define this if you do not want to try to save Emacs's pure data areas
+ − 87 as part of the text segment.
+ − 88
+ − 89 Saving them as text is good because it allows users to share more.
+ − 90
+ − 91 However, on machines that locate the text area far from the data area,
+ − 92 the boundary cannot feasibly be moved. Such machines require
+ − 93 NO_REMAP.
+ − 94
+ − 95 Also, remapping can cause trouble with the built-in startup routine
+ − 96 /lib/crt0.o, which defines `environ' as an initialized variable.
+ − 97 Dumping `environ' as pure does not work! So, to use remapping,
+ − 98 you must write a startup routine for your machine in Emacs's crt0.c.
+ − 99 If NO_REMAP is defined, Emacs uses the system's crt0.o.
+ − 100
+ − 101 * SECTION_ALIGNMENT
+ − 102
+ − 103 Some machines that use COFF executables require that each section
+ − 104 start on a certain boundary *in the COFF file*. Such machines should
+ − 105 define SECTION_ALIGNMENT to a mask of the low-order bits that must be
+ − 106 zero on such a boundary. This mask is used to control padding between
+ − 107 segments in the COFF file.
+ − 108
+ − 109 If SECTION_ALIGNMENT is not defined, the segments are written
+ − 110 consecutively with no attempt at alignment. This is right for
+ − 111 unmodified system V.
+ − 112
+ − 113 * SEGMENT_MASK
+ − 114
+ − 115 Some machines require that the beginnings and ends of segments
+ − 116 *in core* be on certain boundaries. For most machines, a page
+ − 117 boundary is sufficient. That is the default. When a larger
+ − 118 boundary is needed, define SEGMENT_MASK to a mask of
+ − 119 the bits that must be zero on such a boundary.
+ − 120
+ − 121 * A_TEXT_OFFSET(HDR)
+ − 122
+ − 123 Some machines count the a.out header as part of the size of the text
+ − 124 segment (a_text); they may actually load the header into core as the
+ − 125 first data in the text segment. Some have additional padding between
+ − 126 the header and the real text of the program that is counted in a_text.
+ − 127
+ − 128 For these machines, define A_TEXT_OFFSET(HDR) to examine the header
+ − 129 structure HDR and return the number of bytes to add to `a_text'
+ − 130 before writing it (above and beyond the number of bytes of actual
+ − 131 program text). HDR's standard fields are already correct, except that
+ − 132 this adjustment to the `a_text' field has not yet been made;
+ − 133 thus, the amount of offset can depend on the data in the file.
+ − 134
+ − 135 * A_TEXT_SEEK(HDR)
+ − 136
+ − 137 If defined, this macro specifies the number of bytes to seek into the
+ − 138 a.out file before starting to write the text segment.a
+ − 139
+ − 140 * EXEC_MAGIC
+ − 141
+ − 142 For machines using COFF, this macro, if defined, is a value stored
+ − 143 into the magic number field of the output file.
+ − 144
+ − 145 * ADJUST_EXEC_HEADER
+ − 146
+ − 147 This macro can be used to generate statements to adjust or
+ − 148 initialize nonstandard fields in the file header
+ − 149
+ − 150 * ADDR_CORRECT(ADDR)
+ − 151
+ − 152 Macro to correct an int which is the bit pattern of a pointer to a byte
+ − 153 into an int which is the number of a byte.
+ − 154
+ − 155 This macro has a default definition which is usually right.
+ − 156 This default definition is a no-op on most machines (where a
+ − 157 pointer looks like an int) but not on all machines.
+ − 158
+ − 159 */
+ − 160
+ − 161 #include <config.h>
+ − 162 #define PERROR(file) report_error (file, new)
+ − 163
+ − 164 #include <a.out.h>
+ − 165 /* Define getpagesize () if the system does not.
+ − 166 Note that this may depend on symbols defined in a.out.h
+ − 167 */
+ − 168 #include "getpagesize.h"
+ − 169
+ − 170 #include <sys/types.h>
+ − 171 #include <stdio.h>
+ − 172 #include <sys/stat.h>
+ − 173 #include <errno.h>
+ − 174
+ − 175 extern char *start_of_text (); /* Start of text */
+ − 176 extern char *start_of_data (); /* Start of initialized data */
+ − 177
+ − 178 #include <machine/filehdr.h>
+ − 179 #include <machine/opthdr.h>
+ − 180 #include <machine/scnhdr.h>
+ − 181 #include <machine/pte.h>
+ − 182
2286
+ − 183 #include "compiler.h"
+ − 184
428
+ − 185 static long block_copy_start; /* Old executable start point */
+ − 186 static struct filehdr f_hdr; /* File header */
+ − 187 static struct opthdr f_ohdr; /* Optional file header (a.out) */
+ − 188 long bias; /* Bias to add for growth */
+ − 189 #define SYMS_START block_copy_start
+ − 190
+ − 191 static long text_scnptr;
+ − 192 static long data_scnptr;
+ − 193
+ − 194 static int pagemask;
+ − 195 static int pagesz;
+ − 196
+ − 197 static
+ − 198 report_error (file, fd)
+ − 199 char *file;
+ − 200 int fd;
+ − 201 {
+ − 202 if (fd)
+ − 203 close (fd);
563
+ − 204 signal_error (Qio_error, "Failure operating on file",
+ − 205 build_string (file));
428
+ − 206 }
+ − 207
+ − 208 #define ERROR0(msg) report_error_1 (new, msg, 0, 0); return -1
+ − 209 #define ERROR1(msg,x) report_error_1 (new, msg, x, 0); return -1
+ − 210 #define ERROR2(msg,x,y) report_error_1 (new, msg, x, y); return -1
+ − 211
+ − 212 static
+ − 213 report_error_1 (fd, msg, a1, a2)
+ − 214 int fd;
+ − 215 char *msg;
+ − 216 int a1, a2;
+ − 217 {
+ − 218 close (fd);
563
+ − 219 signal_ferror (Qio_error, msg, a1, a2);
428
+ − 220 }
+ − 221
+ − 222 /* ****************************************************************
+ − 223 * unexec
+ − 224 *
+ − 225 * driving logic.
+ − 226 */
+ − 227 unexec (new_name, a_name, data_start, bss_start, entry_address)
+ − 228 char *new_name, *a_name;
+ − 229 unsigned data_start, bss_start, entry_address;
+ − 230 {
+ − 231 int new, a_out = -1;
+ − 232
+ − 233 if (a_name && (a_out = open (a_name, 0)) < 0) {
+ − 234 PERROR (a_name);
+ − 235 }
+ − 236 if ((new = creat (new_name, 0666)) < 0) {
+ − 237 PERROR (new_name);
+ − 238 }
+ − 239
+ − 240 if (make_hdr (new, a_out, data_start, bss_start, entry_address, a_name, new_name) < 0
+ − 241 || copy_text_and_data (new) < 0
+ − 242 || copy_sym (new, a_out, a_name, new_name) < 0 ) {
+ − 243 close (new);
+ − 244 return -1;
+ − 245 }
+ − 246
+ − 247 close (new);
+ − 248 if (a_out >= 0)
+ − 249 close (a_out);
+ − 250 mark_x (new_name);
+ − 251 return 0;
+ − 252 }
+ − 253
+ − 254 /* ****************************************************************
+ − 255 * make_hdr
+ − 256 *
+ − 257 * Make the header in the new a.out from the header in core.
+ − 258 * Modify the text and data sizes.
+ − 259 */
+ − 260
+ − 261 struct scnhdr *stbl; /* Table of all scnhdr's */
+ − 262 struct scnhdr *f_thdr; /* Text section header */
+ − 263 struct scnhdr *f_dhdr; /* Data section header */
+ − 264 struct scnhdr *f_tdhdr; /* Thread Data section header */
+ − 265 struct scnhdr *f_bhdr; /* Bss section header */
+ − 266 struct scnhdr *f_tbhdr; /* Thread Bss section header */
+ − 267
+ − 268 static int
+ − 269 make_hdr (new, a_out, data_start, bss_start, entry_address, a_name, new_name)
+ − 270 int new, a_out;
2286
+ − 271 unsigned data_start, bss_start;
+ − 272 unsigned UNUSED (entry_address);
428
+ − 273 char *a_name;
+ − 274 char *new_name;
+ − 275 {
+ − 276 int scns;
+ − 277 unsigned int bss_end;
+ − 278 unsigned int eo_data; /* End of initialized data in new exec file */
+ − 279 int scntype; /* Section type */
+ − 280 int i; /* Var for sorting by vaddr */
+ − 281 struct scnhdr scntemp; /* For swapping entries in sort */
+ − 282 extern char *start_of_data();
+ − 283
+ − 284 pagemask = (pagesz = getpagesize()) - 1;
+ − 285
+ − 286 /* Adjust text/data boundary. */
+ − 287 if (!data_start)
+ − 288 data_start = (unsigned) start_of_data ();
+ − 289
+ − 290 data_start = data_start & ~pagemask; /* (Down) to page boundary. */
+ − 291
+ − 292 bss_end = (sbrk(0) + pagemask) & ~pagemask;
+ − 293
+ − 294 /* Adjust data/bss boundary. */
+ − 295 if (bss_start != 0) {
+ − 296 bss_start = (bss_start + pagemask) & ~pagemask;/* (Up) to page bdry. */
+ − 297 if (bss_start > bss_end) {
+ − 298 ERROR1 ("unexec: Specified bss_start (%x) is past end of program",
+ − 299 bss_start);
+ − 300 }
+ − 301 } else
+ − 302 bss_start = bss_end;
+ − 303
+ − 304 if (data_start > bss_start) { /* Can't have negative data size. */
+ − 305 ERROR2 ("unexec: data_start (%x) can't be greater than bss_start (%x)",
+ − 306 data_start, bss_start);
+ − 307 }
+ − 308
+ − 309 /* Salvage as much info from the existing file as possible */
+ − 310 if (a_out < 0) {
+ − 311 ERROR0 ("can't build a COFF file from scratch yet");
+ − 312 /*NOTREACHED*/
+ − 313 }
+ − 314
+ − 315 if (read (a_out, &f_hdr, sizeof (f_hdr)) != sizeof (f_hdr)) {
+ − 316 PERROR (a_name);
+ − 317 }
+ − 318 block_copy_start += sizeof (f_hdr);
+ − 319 if (f_hdr.h_opthdr > 0) {
+ − 320 if (read (a_out, &f_ohdr, sizeof (f_ohdr)) != sizeof (f_ohdr)) {
+ − 321 PERROR (a_name);
+ − 322 }
+ − 323 block_copy_start += sizeof (f_ohdr);
+ − 324 }
+ − 325
+ − 326 /* Allocate room for scn headers */
+ − 327 stbl = (struct scnhdr *)malloc( sizeof(struct scnhdr) * f_hdr.h_nscns );
+ − 328 if( stbl == NULL ) {
+ − 329 ERROR0( "unexec: malloc of stbl failed" );
+ − 330 }
+ − 331
+ − 332 f_tdhdr = f_tbhdr = NULL;
+ − 333
+ − 334 /* Loop through section headers, copying them in */
+ − 335 for (scns = 0; scns < f_hdr.h_nscns; scns++) {
+ − 336
+ − 337 if( read( a_out, &stbl[scns], sizeof(*stbl)) != sizeof(*stbl)) {
+ − 338 PERROR (a_name);
+ − 339 }
+ − 340
+ − 341 scntype = stbl[scns].s_flags & S_TYPMASK; /* What type of section */
+ − 342
+ − 343 if( stbl[scns].s_scnptr > 0L) {
+ − 344 if( block_copy_start < stbl[scns].s_scnptr + stbl[scns].s_size )
+ − 345 block_copy_start = stbl[scns].s_scnptr + stbl[scns].s_size;
+ − 346 }
+ − 347
+ − 348 if( scntype == S_TEXT) {
+ − 349 f_thdr = &stbl[scns];
+ − 350 } else if( scntype == S_DATA) {
+ − 351 f_dhdr = &stbl[scns];
+ − 352 #ifdef S_TDATA
+ − 353 } else if( scntype == S_TDATA ) {
+ − 354 f_tdhdr = &stbl[scns];
+ − 355 } else if( scntype == S_TBSS ) {
+ − 356 f_tbhdr = &stbl[scns];
+ − 357 #endif /* S_TDATA (thread stuff) */
+ − 358
+ − 359 } else if( scntype == S_BSS) {
+ − 360 f_bhdr = &stbl[scns];
+ − 361 }
+ − 362
+ − 363 }
+ − 364
+ − 365 /* We will now convert TEXT and DATA into TEXT, BSS into DATA, and leave
+ − 366 * all thread stuff alone.
+ − 367 */
+ − 368
+ − 369 /* Now we alter the contents of all the f_*hdr variables
+ − 370 to correspond to what we want to dump. */
+ − 371
+ − 372 f_thdr->s_vaddr = (long) start_of_text ();
+ − 373 f_thdr->s_size = data_start - f_thdr->s_vaddr;
+ − 374 f_thdr->s_scnptr = pagesz;
+ − 375 f_thdr->s_relptr = 0;
+ − 376 f_thdr->s_nrel = 0;
+ − 377
+ − 378 eo_data = f_thdr->s_scnptr + f_thdr->s_size;
+ − 379
+ − 380 if( f_tdhdr ) { /* Process thread data */
+ − 381
+ − 382 f_tdhdr->s_vaddr = data_start;
+ − 383 f_tdhdr->s_size += f_dhdr->s_size - (data_start - f_dhdr->s_vaddr);
+ − 384 f_tdhdr->s_scnptr = eo_data;
+ − 385 f_tdhdr->s_relptr = 0;
+ − 386 f_tdhdr->s_nrel = 0;
+ − 387
+ − 388 eo_data += f_tdhdr->s_size;
+ − 389
+ − 390 /* And now for DATA */
+ − 391
+ − 392 f_dhdr->s_vaddr = f_bhdr->s_vaddr; /* Take BSS start address */
+ − 393 f_dhdr->s_size = bss_end - f_bhdr->s_vaddr;
+ − 394 f_dhdr->s_scnptr = eo_data;
+ − 395 f_dhdr->s_relptr = 0;
+ − 396 f_dhdr->s_nrel = 0;
+ − 397
+ − 398 eo_data += f_dhdr->s_size;
+ − 399
+ − 400 } else {
+ − 401
+ − 402 f_dhdr->s_vaddr = data_start;
+ − 403 f_dhdr->s_size = bss_start - data_start;
+ − 404 f_dhdr->s_scnptr = eo_data;
+ − 405 f_dhdr->s_relptr = 0;
+ − 406 f_dhdr->s_nrel = 0;
+ − 407
+ − 408 eo_data += f_dhdr->s_size;
+ − 409
+ − 410 }
+ − 411
+ − 412 f_bhdr->s_vaddr = bss_start;
+ − 413 f_bhdr->s_size = bss_end - bss_start + pagesz /* fudge */;
+ − 414 f_bhdr->s_scnptr = 0;
+ − 415 f_bhdr->s_relptr = 0;
+ − 416 f_bhdr->s_nrel = 0;
+ − 417
+ − 418 text_scnptr = f_thdr->s_scnptr;
+ − 419 data_scnptr = f_dhdr->s_scnptr;
+ − 420 bias = eo_data - block_copy_start;
+ − 421
+ − 422 if (f_ohdr.o_symptr > 0L) {
+ − 423 f_ohdr.o_symptr += bias;
+ − 424 }
+ − 425
+ − 426 if (f_hdr.h_strptr > 0) {
+ − 427 f_hdr.h_strptr += bias;
+ − 428 }
+ − 429
+ − 430 if (write (new, &f_hdr, sizeof (f_hdr)) != sizeof (f_hdr)) {
+ − 431 PERROR (new_name);
+ − 432 }
+ − 433
+ − 434 if (write (new, &f_ohdr, sizeof (f_ohdr)) != sizeof (f_ohdr)) {
+ − 435 PERROR (new_name);
+ − 436 }
+ − 437
+ − 438 for( scns = 0; scns < f_hdr.h_nscns; scns++ ) {
+ − 439
+ − 440 /* This is a cheesy little loop to write out the section headers
+ − 441 * in order of increasing virtual address. Dull but effective.
+ − 442 */
+ − 443
+ − 444 for( i = scns+1; i < f_hdr.h_nscns; i++ ) {
+ − 445 if( stbl[i].s_vaddr < stbl[scns].s_vaddr ) { /* Swap */
+ − 446 scntemp = stbl[i];
+ − 447 stbl[i] = stbl[scns];
+ − 448 stbl[scns] = scntemp;
+ − 449 }
+ − 450 }
+ − 451
+ − 452 }
+ − 453
+ − 454 for( scns = 0; scns < f_hdr.h_nscns; scns++ ) {
+ − 455
+ − 456 if( write( new, &stbl[scns], sizeof(*stbl)) != sizeof(*stbl)) {
+ − 457 PERROR (new_name);
+ − 458 }
+ − 459
+ − 460 }
+ − 461
+ − 462 return (0);
+ − 463
+ − 464 }
+ − 465
+ − 466 /* ****************************************************************
+ − 467 * copy_text_and_data
+ − 468 *
+ − 469 * Copy the text and data segments from memory to the new a.out
+ − 470 */
+ − 471 static int
+ − 472 copy_text_and_data (new)
+ − 473 int new;
+ − 474 {
+ − 475 int scns;
+ − 476
+ − 477 for( scns = 0; scns < f_hdr.h_nscns; scns++ )
+ − 478 write_segment( new, &stbl[scns] );
+ − 479
+ − 480 return 0;
+ − 481 }
+ − 482
+ − 483 write_segment( new, sptr )
+ − 484 int new;
+ − 485 struct scnhdr *sptr;
+ − 486 {
+ − 487 char *ptr, *end;
+ − 488 int nwrite, ret;
+ − 489 char buf[80];
+ − 490 extern int errno;
+ − 491 char zeros[128];
+ − 492
+ − 493 if( sptr->s_scnptr == 0 )
+ − 494 return; /* Nothing to do */
+ − 495
+ − 496 if( lseek( new, (long) sptr->s_scnptr, 0 ) == -1 )
+ − 497 PERROR( "unexecing" );
+ − 498
647
+ − 499 memset (zeros, 0, sizeof (zeros));
428
+ − 500
+ − 501 ptr = (char *) sptr->s_vaddr;
+ − 502 end = ptr + sptr->s_size;
+ − 503
+ − 504 while( ptr < end ) {
+ − 505
+ − 506 /* distance to next multiple of 128. */
+ − 507 nwrite = (((int) ptr + 128) & -128) - (int) ptr;
+ − 508 /* But not beyond specified end. */
+ − 509 if (nwrite > end - ptr) nwrite = end - ptr;
+ − 510 ret = write (new, ptr, nwrite);
+ − 511 /* If write gets a page fault, it means we reached
+ − 512 a gap between the old text segment and the old data segment.
+ − 513 This gap has probably been remapped into part of the text segment.
+ − 514 So write zeros for it. */
+ − 515 if (ret == -1 && errno == EFAULT)
+ − 516 write (new, zeros, nwrite);
+ − 517 else if (nwrite != ret) {
+ − 518 sprintf (buf,
+ − 519 "unexec write failure: addr 0x%x, fileno %d, size 0x%x, wrote 0x%x, errno %d",
+ − 520 ptr, new, nwrite, ret, errno);
+ − 521 PERROR (buf);
+ − 522 }
+ − 523 ptr += nwrite;
+ − 524 }
+ − 525 }
+ − 526
+ − 527 /* ****************************************************************
+ − 528 * copy_sym
+ − 529 *
+ − 530 * Copy the relocation information and symbol table from the a.out to the new
+ − 531 */
+ − 532 static int
+ − 533 copy_sym (new, a_out, a_name, new_name)
+ − 534 int new, a_out;
+ − 535 char *a_name, *new_name;
+ − 536 {
+ − 537 char page[1024];
+ − 538 int n;
+ − 539
+ − 540 if (a_out < 0)
+ − 541 return 0;
+ − 542
+ − 543 if (SYMS_START == 0L)
+ − 544 return 0;
+ − 545
+ − 546 lseek (a_out, SYMS_START, 0); /* Position a.out to symtab. */
+ − 547 lseek( new, (long)f_ohdr.o_symptr, 0 );
+ − 548
647
+ − 549 while ((n = read (a_out, page, sizeof (page))) > 0) {
428
+ − 550 if (write (new, page, n) != n) {
+ − 551 PERROR (new_name);
+ − 552 }
+ − 553 }
+ − 554 if (n < 0) {
+ − 555 PERROR (a_name);
+ − 556 }
+ − 557 return 0;
+ − 558 }
+ − 559
+ − 560 /* ****************************************************************
+ − 561 * mark_x
+ − 562 *
+ − 563 * After successfully building the new a.out, mark it executable
+ − 564 */
+ − 565 static
+ − 566 mark_x (name)
+ − 567 char *name;
+ − 568 {
+ − 569 struct stat sbuf;
+ − 570 int um;
+ − 571 int new = 0; /* for PERROR */
+ − 572
+ − 573 um = umask (777);
+ − 574 umask (um);
+ − 575 if (stat (name, &sbuf) == -1) {
+ − 576 PERROR (name);
+ − 577 }
+ − 578 sbuf.st_mode |= 0111 & ~um;
+ − 579 if (chmod (name, sbuf.st_mode) == -1)
+ − 580 PERROR (name);
+ − 581 }
+ − 582
+ − 583 /* Find the first pty letter. This is usually 'p', as in ptyp0, but
+ − 584 is sometimes configured down to 'm', 'n', or 'o' for some reason. */
+ − 585
+ − 586 first_pty_letter ()
+ − 587 {
+ − 588 struct stat buf;
+ − 589 char pty_name[16];
+ − 590 char c;
+ − 591
+ − 592 for (c = 'o'; c >= 'a'; c--)
+ − 593 {
+ − 594 sprintf (pty_name, "/dev/pty%c0", c);
+ − 595 if (stat (pty_name, &buf) < 0)
+ − 596 return c + 1;
+ − 597 }
+ − 598 return 'a';
+ − 599 }
+ − 600