Advanced Programming in the UNIX Environment: Second Edition [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Advanced Programming in the UNIX Environment: Second Edition [Electronic resources] - نسخه متنی

W. Richard Stevens; Stephen A. Rago

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید



2.5. Limits


The implementations define many magic numbers and constants. Many of these have been hard coded into programs or were determined using ad hoc techniques. With the various standardization efforts that we've described, more portable methods are now provided to determine these magic numbers and implementation-defined limits, greatly aiding the portability of our software.

Two types of limits are needed:

  • Compile-time limits (e.g., what's the largest value of a short integer?)

  • Runtime limits (e.g., how many characters in a filename?)

  • Compile-time limits can be defined in headers that any program can include at compile time. But runtime limits require the process to call a function to obtain the value of the limit.

    Additionally, some limits can be fixed on a given implementationand could therefore be defined statically in a headeryet vary on another implementation and would require a runtime function call. An example of this type of limit is the maximum number of characters in a filename. Before SVR4, System V historically allowed only 14 characters in a filename, whereas BSD-derived systems increased this number to 255. Most UNIX System implementations these days support multiple file system types, and each type has its own limit. This is the case of a runtime limit that depends on where in the file system the file in question is located. A filename in the root file system, for example, could have a 14-character limit, whereas a filename in another file system could have a 255-character limit.

    To solve these problems, three types of limits are provided:

  • Compile-time limits (headers)

  • Runtime limits that are not associated with a file or directory (the sysconf function)

  • Runtime limits that are associated with a file or a directory (the pathconf and fpathconf functions)

  • To further confuse things, if a particular runtime limit does not vary on a given system, it can be defined statically in a header. If it is not defined in a header, however, the application must call one of the three conf functions (which we describe shortly) to determine its value at runtime.


    2.5.1. ISO C Limits


    Section 5.13.

    In Figure 2.7, we show the values of FOPEN_MAX and TMP_MAX on the four platforms we discuss in this book.

    Figure 2.7. ISO limits on various platforms

    Limit

    FreeBSD 5.2.1

    Linux 2.4.22

    Mac OS X 10.3

    Solaris 9

    FOPEN_MAX

    20

    16

    20

    20

    TMP_MAX

    308,915,776

    238,328

    308,915,776

    17,576

    ISO C also defines the constant FILENAME_MAX, but we avoid using it, because some operating system implementations historically have defined it to be too small to be of use.


    2.5.2. POSIX Limits


    POSIX.1 defines numerous constants that deal with implementation limits of the operating system. Unfortunately, this is one of the more confusing aspects of POSIX.1. Although POSIX.1 defines numerous limits and constants, we'll only concern ourselves with the ones that affect the base POSIX.1 interfaces. These limits and constants are divided into the following five categories:

  • Invariant minimum values: the 19 constants in Figure 2.8

  • Invariant value: SSIZE_MAX

  • Runtime increasable values: CHARCLASS_NAME_MAX, COLL_WEIGHTS_MAX, LINE_MAX, NGROUPS_MAX, and RE_DUP_MAX

  • Runtime invariant values, possibly indeterminate: ARG_MAX, CHILD_MAX, HOST_NAME_MAX, LOGIN_NAME_MAX, OPEN_MAX, PAGESIZE, RE_DUP_MAX, STREAM_MAX, SYMLOOP_MAX, TTY_NAME_MAX, and TZNAME_MAX

  • Pathname variable values, possibly indeterminate: FILESIZEBITS, LINK_MAX, MAX_CANON, MAX_INPUT, NAME_MAX, PATH_MAX, PIPE_BUF, and SYMLINK_MAX

  • Figure 2.8. POSIX.1 invariant minimum values from <limits.h>

    Name

    Description: minimum acceptable value for

    Value

    _POSIX_ARG_MAX

    length of arguments to exec functions

    4,096

    _POSIX_CHILD_MAX

    number of child processes per real user ID

    25

    _POSIX_HOST_NAME_MAX

    maximum length of a host name as returned by gethostname

    255

    _POSIX_LINK_MAX

    number of links to a file

    8

    _POSIX_LOGIN_NAME_MAX

    maximum length of a login name

    9

    _POSIX_MAX_CANON

    number of bytes on a terminal's canonical input queue

    255

    _POSIX_MAX_INPUT

    space available on a terminal's input queue

    255

    _POSIX_NAME_MAX

    number of bytes in a filename, not including the terminating null

    14

    _POSIX_NGROUPS_MAX

    number of simultaneous supplementary group IDs per process

    8

    _POSIX_OPEN_MAX

    number of open files per process

    20

    _POSIX_PATH_MAX

    number of bytes in a pathname, including the terminating null

    256

    _POSIX_PIPE_BUF

    number of bytes that can be written atomically to a pipe

    512

    _POSIX_RE_DUP_MAX

    number of repeated occurrences of a basic regular expression permitted by the regexec and regcomp functions when using the interval notation \{m,n\}

    255

    _POSIX_SSIZE_MAX

    value that can be stored in ssize_t object

    32,767

    _POSIX_STREAM_MAX

    number of standard I/O streams a process can have open at once

    8

    _POSIX_SYMLINK_MAX

    number of bytes in a symbolic link

    255

    _POSIX_SYMLOOP_MAX

    number of symbolic links that can be traversed during pathname resolution

    8

    _POSIX_TTY_NAME_MAX

    length of a terminal device name, including the terminating null

    9

    _POSIX_TZNAME_MAX

    number of bytes for the name of a time zone

    6

    Of these 44 limits and constants, some may be defined in <limits.h>, and others may or may not be defined, depending on certain conditions. We describe the limits and constants that may or may not be defined in Section 2.5.4, when we describe the sysconf, pathconf, and fpathconf functions. The 19 invariant minimum values are shown in Figure 2.8.

    These values are invariant; they do not change from one system to another. They specify the most restrictive values for these features. A conforming POSIX.1 implementation must provide values that are at least this large. This is why they are called minimums, although their names all contain MAX. Also, to ensure portability, a strictly-conforming application must not require a larger value. We describe what each of these constants refers to as we proceed through the text.

    A strictly-conforming POSIX application is different from an application that is merely POSIX conforming. A POSIX-conforming application uses only interfaces defined in IEEE Standard 1003.1-2001. A strictly-conforming application is a POSIX-conforming application that does not rely on any undefined behavior, does not use any obsolescent interfaces, and does not require values of constants larger than the minimums shown in Figure 2.8.

    Unfortunately, some of these invariant minimum values are too small to be of practical use. For example, most UNIX systems today provide far more than 20 open files per process. Also, the minimum limit of 255 for _POSIX_PATH_MAX is too small. Pathnames can exceed this limit. This means that we can't use the two constants _POSIX_OPEN_MAX and _POSIX_PATH_MAX as array sizes at compile time.

    Each of the 19 invariant minimum values in Figure 2.8 has an associated implementation value whose name is formed by removing the _POSIX_ prefix from the name in Figure 2.8. The names without the leading _POSIX_ were intended to be the actual values that a given implementation supports. (These 19 implementation values are items 25 from our list earlier in this section: the invariant value, the runtime increasable value, the runtime invariant values, and the pathname variable values.) The problem is that not all of the 19 implementation values are guaranteed to be defined in the <limits.h> header.

    For example, a particular value may not be included in the header if its actual value for a given process depends on the amount of memory on the system. If the values are not defined in the header, we can't use them as array bounds at compile time. So, POSIX.1 decided to provide three runtime functions for us to callsysconf, pathconf, and fpathconfto determine the actual implementation value at runtime. There is still a problem, however, because some of the values are defined by POSIX.1 as being possibly "indeterminate" (logically infinite). This means that the value has no practical upper bound. On Linux, for example, the number of iovec structures you can use with readv or writev is limited only by the amount of memory on the system. Thus, IOV_MAX is considered indeterminate on Linux. We'll return to this problem of indeterminate runtime limits in Section 2.5.5.


    2.5.3. XSI Limits


    The XSI also defines constants that deal with implementation limits. They include:

  • Invariant minimum values: the ten constants in Figure 2.9

  • Numerical limits: LONG_BIT and WORD_BIT

  • Runtime invariant values, possibly indeterminate: ATEXIT_MAX, IOV_MAX, and PAGE_SIZE

  • Figure 2.9. XSI invariant minimum values from <limits.h>

    Name

    Description

    Minimum acceptable value

    Typical value

    NL_ARGMAX

    maximum value of digit in calls to printf and scanf

    9

    9

    NL_LANGMAX

    maximum number of bytes in LANG environment variable

    14

    14

    NL_MSGMAX

    maximum message number

    32,767

    32,767

    NL_NMAX

    maximum number of bytes in

    N -to-1 mapping characters

    (none specified)

    1

    NL_SETMAX

    maximum set number

    255

    255

    NL_TEXTMAX

    maximum number of bytes in a message string

    _POSIX2_LINE_MAX

    2,048

    NZERO

    default process priority

    20

    20

    _XOPEN_IOV_MAX

    maximum number of iovec structures that can be used with readv or writev

    16

    16

    _XOPEN_NAME_MAX

    number of bytes in a filename

    255

    255

    _XOPEN_PATH_MAX

    number of bytes in a pathname

    1,024

    1,024

    The invariant minimum values are listed in Figure 2.9. Many of these values deal with message catalogs. The last two illustrate the situation in which the POSIX.1 minimums were too smallpresumably to allow for embedded POSIX.1 implementationsso the Single UNIX Specification added symbols with larger minimum values for XSI-conforming systems.


    2.5.4. sysconf, pathconf, and fpathconf Functions


    We've listed various minimum values that an implementation must support, but how do we find out the limits that a particular system actually supports? As we mentioned earlier, some of these limits might be available at compile time; others must be determined at runtime. We've also mentioned that some don't change in a given system, whereas others can change because they are associated with a file or directory. The runtime limits are obtained by calling one of the following three functions.

    #include <unistd.h>
    long sysconf(int

    name );
    long pathconf(const char *

    pathname , int

    name );
    long fpathconf(int

    filedes , int

    name );

    All three return: corresponding value if OK, 1 on error (see later)

    The difference between the last two functions is that one takes a pathname as its argument and the other takes a file descriptor argument.

    Figure 2.10 lists the

    name arguments that sysconf uses to identify system limits. Constants beginning with _SC_ are used as arguments to sysconf to identify the runtime limit. Figure 2.11 lists the

    name arguments that are used by pathconf and fpathconf to identify system limits. Constants beginning with _PC_ are used as arguments to pathconf and fpathconf to identify the runtime limit.

    Figure 2.10. Limits and

    name arguments to sysconf

    Name of limit

    Description

    name argument

    ARG_MAX

    maximum length, in bytes, of arguments to the exec functions

    _SC_ARG_MAX

    ATEXIT_MAX

    maximum number of functions that can be registered with the atexit function

    _SC_ATEXIT_MAX

    CHILD_MAX

    maximum number of processes per real user ID

    _SC_CHILD_MAX

    clock ticks/second

    number of clock ticks per second

    _SC_CLK_TCK

    COLL_WEIGHTS_MAX

    maximum number of weights that can be assigned to an entry of the LC_COLLATE order keyword in the locale definition file

    _SC_COLL_WEIGHTS_MAX

    HOST_NAME_MAX

    maximum length of a host name as returned by gethostname

    _SC_HOST_NAME_MAX

    IOV_MAX

    maximum number of iovec structures that can be used with readv or writev

    _SC_IOV_MAX

    LINE_MAX

    maximum length of a utility's input line

    _SC_LINE_MAX

    LOGIN_NAME_MAX

    maximum length of a login name

    _SC_LOGIN_NAME_MAX

    NGROUPS_MAX

    maximum number of simultaneous supplementary process group IDs per process

    _SC_NGROUPS_MAX

    OPEN_MAX

    maximum number of open files per process

    _SC_OPEN_MAX

    PAGESIZE

    system memory page size, in bytes

    _SC_PAGESIZE

    PAGE_SIZE

    system memory page size, in bytes

    _SC_PAGE_SIZE

    RE_DUP_MAX

    number of repeated occurrences of a basic regular expression permitted by the regexec and regcomp functions when using the interval notation \{m,n\}

    _SC_RE_DUP_MAX

    STREAM_MAX

    maximum number of standard I/O streams per process at any given time; if defined, it must have the same value as FOPEN_MAX

    _SC_STREAM_MAX

    SYMLOOP_MAX

    number of symbolic links that can be traversed during pathname resolution

    _SC_SYMLOOP_MAX

    TTY_NAME_MAX

    length of a terminal device name, including the terminating null

    _SC_TTY_NAME_MAX

    TZNAME_MAX

    maximum number of bytes for the name of a time zone

    _SC_TZNAME_MAX

    Figure 2.11. Limits and

    name arguments to pathconf and fpathconf

    Name of limit

    Description

    name argument

    FILESIZEBITS

    minimum number of bits needed to represent, as a signed integer value, the maximum size of a regular file allowed in the specified directory

    _PC_FILESIZEBITS

    LINK_MAX

    maximum value of a file's link count

    _PC_LINK_MAX

    MAX_CANON

    maximum number of bytes on a terminal's canonical input queue

    _PC_MAX_CANON

    MAX_INPUT

    number of bytes for which space is available on terminal's input queue

    _PC_MAX_INPUT

    NAME_MAX

    maximum number of bytes in a filename (does not include a null at end)

    _PC_NAME_MAX

    PATH_MAX

    maximum number of bytes in a relative pathname, including the terminating null

    _PC_PATH_MAX

    PIPE_BUF

    maximum number of bytes that can be written atomically to a pipe

    _PC_PIPE_BUF

    SYMLINK_MAX

    number of bytes in a symbolic link

    _PC_SYMLINK_MAX

    Section 8.16).

    There are some restrictions for the

    pathname argument to pathconf and the

    filedes argument to fpathconf. If any of these restrictions isn't met, the results are undefined.

  • The referenced file for _PC_MAX_CANON and _PC_MAX_INPUT must be a terminal file.

  • The referenced file for _PC_LINK_MAX can be either a file or a directory. If the referenced file is a directory, the return value applies to the directory itself, not to the filename entries within the directory.

  • The referenced file for _PC_FILESIZEBITS and _PC_NAME_MAX must be a directory. The return value applies to filenames within the directory.

  • The referenced file for _PC_PATH_MAX must be a directory. The value returned is the maximum length of a relative pathname when the specified directory is the working directory. (Unfortunately, this isn't the real maximum length of an absolute pathname, which is what we want to know. We'll return to this problem in Section 2.5.5.)

  • The referenced file for _PC_PIPE_BUF must be a pipe, FIFO, or directory. In the first two cases (pipe or FIFO) the return value is the limit for the referenced pipe or FIFO. For the other case (a directory) the return value is the limit for any FIFO created in that directory.

  • The referenced file for _PC_SYMLINK_MAX must be a directory. The value returned is the maximum length of the string that a symbolic link in that directory can contain.


  • Example

    The awk(1) program shown in Figure 2.12 builds a C program that prints the value of each pathconf and sysconf symbol.

    The awk program reads two input filespathconf.sym and sysconf.symthat contain lists of the limit name and symbol, separated by tabs. All symbols are not defined on every platform, so the awk program surrounds each call to pathconf and sysconf with the necessary #ifdef statements.

    For example, the awk program transforms a line in the input file that looks like

    NAME_MAX _PC_NAME_MAX

    into the following C code:

    #ifdef NAME_MAX
    printf("NAME_MAX is defined to be %d\n", NAME_MAX+0);
    #else
    printf("no symbol for NAME_MAX\n");
    #endif
    #ifdef _PC_NAME_MAX
    pr_pathconf("NAME_MAX =", argv[1], _PC_NAME_MAX);
    #else
    printf("no symbol for _PC_NAME_MAX\n");
    #endif

    Section 4.14 that UFS is the SVR4 implementation of the Berkeley fast file system. PCFS is the MS-DOS FAT file system implementation for Solaris.


    Figure 2.12. Build C program to print all supported configuration limits

    BEGIN {
    printf("#include \"apue.h\"\n")
    printf("#include <errno.h>\n")
    printf("#include <limits.h>\n")
    printf("\n")
    printf("static void pr_sysconf(char *, int);\n")
    printf("static void pr_pathconf(char *, char *, int);\n")
    printf("\n")
    printf("int\n")
    printf("main(int argc, char *argv[])\n")
    printf("{\n")
    printf("\tif (argc != 2)\n")
    printf("\t\terr_quit(\"usage: a.out <dirname>\");\n\n")
    FS="\t+"
    while (getline <"sysconf.sym" > 0) {
    printf("#ifdef %s\n", $1)
    printf("\tprintf(\"%s defined to be %%d\\n\", %s+0);\n", $1, $1)
    printf("#else\n")
    printf("\tprintf(\"no symbol for %s\\n\");\n", $1)
    printf("#endif\n")
    printf("#ifdef %s\n", $2)
    printf("\tpr_sysconf(\"%s =\", %s);\n", $1, $2)
    printf("#else\n")
    printf("\tprintf(\"no symbol for %s\\n\");\n", $2)
    printf("#endif\n")
    }
    close("sysconf.sym")
    while (getline <"pathconf.sym" > 0) {
    printf("#ifdef %s\n", $1)
    printf("\tprintf(\"%s defined to be %%d\\n\", %s+0);\n", $1, $1)
    printf("#else\n")
    printf("\tprintf(\"no symbol for %s\\n\");\n", $1)
    printf("#endif\n")
    printf("#ifdef %s\n", $2)
    printf("\tpr_pathconf(\"%s =\", argv[1], %s);\n", $1, $2)
    printf("#else\n")
    printf("\tprintf(\"no symbol for %s\\n\");\n", $2)
    printf("#endif\n")
    }
    close("pathconf.sym")
    exit
    }
    END {
    printf("\texit(0);\n")
    printf("}\n\n")
    printf("static void\n")
    printf("pr_sysconf(char *mesg, int name)\n")
    printf("{\n")
    printf("\tlong val;\n\n")
    printf("\tfputs(mesg, stdout);\n")
    printf("\terrno = 0;\n")
    printf("\tif ((val = sysconf(name)) < 0) {\n")
    printf("\t\tif (errno != 0) {\n")
    printf("\t\t\tif (errno == EINVAL)\n")
    printf("\t\t\t\tfputs(\" (not supported)\\n\", stdout);\n")
    printf("\t\t\telse\n")
    printf("\t\t\t\terr_sys(\"sysconf error\");\n")
    printf("\t\t} else {\n")
    printf("\t\t\tfputs(\" (no limit)\\n\", stdout);\n")
    printf("\t\t}\n")
    printf("\t} else {\n")
    printf("\t\tprintf(\" %%ld\\n\", val);\n")
    printf("\t}\n")
    printf("}\n\n")
    printf("static void\n")
    printf("pr_pathconf(char *mesg, char *path, int name)\n")
    printf("{\n")
    printf("\tlong val;\n")
    printf("\n")
    printf("\tfputs(mesg, stdout);\n")
    printf("\terrno = 0;\n")
    printf("\tif ((val = pathconf(path, name)) < 0) {\n")
    printf("\t\tif (errno != 0) {\n")
    printf("\t\t\tif (errno == EINVAL)\n")
    printf("\t\t\t\tfputs(\" (not supported)\\n\", stdout);\n")
    printf("\t\t\telse\n")
    printf("\t\t\t\terr_sys(\"pathconf error, path = %%s\", path);\n")
    printf("\t\t} else {\n")
    printf("\t\t\tfputs(\" (no limit)\\n\", stdout);\n")
    printf("\t\t}\n")
    printf("\t} else {\n")
    printf("\t\tprintf(\" %%ld\\n\", val);\n")
    printf("\t}\n")
    printf("}\n")
    }


    Figure 2.13. Print all possible sysconf and pathconf values

    #include "apue.h"
    #include <errno.h>
    #include <limits.h>
    static void pr_sysconf(char *, int);
    static void pr_pathconf(char *, char *, int);
    int
    main(int argc, char *argv[])
    {
    if (argc != 2)
    err_quit("usage: a.out <dirname>");
    #ifdef ARG_MAX
    printf("ARG_MAX defined to be %d\n", ARG_MAX+0);
    #else
    printf("no symbol for ARG_MAX\n");
    #endif
    #ifdef _SC_ARG_MAX
    pr_sysconf("ARG_MAX =", _SC_ARG_MAX);
    #else
    printf("no symbol for _SC_ARG_MAX\n");
    #endif
    /* similar processing for all the rest of the sysconf symbols... */
    #ifdef MAX_CANON
    printf("MAX_CANON defined to be %d\n", MAX_CANON+0);
    #else
    printf("no symbol for MAX_CANON\n");
    #endif
    #ifdef _PC_MAX_CANON
    pr_pathconf("MAX_CANON =", argv[1], _PC_MAX_CANON);
    #else
    printf("no symbol for _PC_MAX_CANON\n");
    #endif
    /* similar processing for all the rest of the pathconf symbols... */
    exit(0);
    }
    static void
    pr_sysconf(char *mesg, int name)
    {
    long val;
    fputs(mesg, stdout);
    errno = 0;
    if ((val = sysconf(name)) < 0) {
    if (errno != 0) {
    if (errno == EINVAL)
    fputs(" (not supported)\n", stdout);
    else
    err_sys("sysconf error");
    } else {
    fputs(" (no limit)\n", stdout);
    }
    } else {
    printf(" %ld\n", val);
    }
    }
    static void
    pr_pathconf(char *mesg, char *path, int name)
    {
    long val;
    fputs(mesg, stdout);
    errno = 0;
    if ((val = pathconf(path, name)) < 0) {
    if (errno != 0) {
    if (errno == EINVAL)
    fputs(" (not supported)\n", stdout);
    else
    err_sys("pathconf error, path = %s", path);
    } else {
    fputs(" (no limit)\n", stdout);
    }
    } else {
    printf(" %ld\n", val);
    }
    }

    Figure 2.14. Examples of configuration limits

    Limit

    FreeBSD 5.2.1

    Linux 2.4.22

    Mac OS X 10.3

    Solaris 9

    UFS file system

    PCFS file system

    ARG_MAX

    65,536

    131,072

    262,144

    1,048,320

    1,048,320

    ATEXIT_MAX

    32

    2,147,483,647

    no symbol

    no limit

    no limit

    CHARCLASS_NAME_MAX

    no symbol

    2,048

    no symbol

    14

    14

    CHILD_MAX

    867

    999

    100

    7,877

    7,877

    clock ticks/second

    128

    100

    100

    100

    100

    COLL_WEIGHTS_MAX

    0

    255

    2

    10

    10

    FILESIZEBITS

    unsupported

    64

    no symbol

    41

    unsupported

    HOST_NAME_MAX

    255

    unsupported

    no symbol

    no symbol

    no symbol

    IOV_MAX

    1,024

    no limit

    no symbol

    16

    16

    LINE_MAX

    2,048

    2,048

    2,048

    2,048

    2,048

    LINK_MAX

    32,767

    32,000

    32,767

    32,767

    1

    LOGIN_NAME_MAX

    17

    256

    no symbol

    9

    9

    MAX_CANON

    255

    255

    255

    256

    256

    MAX_INPUT

    255

    255

    255

    512

    512

    NAME_MAX

    255

    255

    765

    255

    8

    NGROUPS_MAX

    16

    32

    16

    16

    16

    OPEN_MAX

    1,735

    1,024

    256

    256

    256

    PAGESIZE

    4,096

    4,096

    4,096

    8,192

    8,192

    PAGE_SIZE

    4,096

    4,096

    no symbol

    8,192

    8,192

    PATH_MAX

    1,024

    4,096

    1,024

    1,024

    1,024

    PIPE_BUF

    512

    4,096

    512

    5,120

    5,120

    RE_DUP_MAX

    255

    32,767

    255

    255

    255

    STREAM_MAX

    1,735

    16

    20

    256

    256

    SYMLINK_MAX

    unsupported

    no limit

    no symbol

    no symbol

    no symbol

    SYMLOOP_MAX

    32

    no limit

    no symbol

    no symbol

    no symbol

    TTY_NAME_MAX

    255

    32

    no symbol

    128

    128

    TZNAME_MAX

    255

    6

    255

    no limit

    no limit


    2.5.5. Indeterminate Runtime Limits


    We mentioned that some of the limits can be indeterminate. The problem we encounter is that if these limits aren't defined in the <limits.h> header, we can't use them at compile time. But they might not be defined at runtime if their value is indeterminate! Let's look at two specific cases: allocating storage for a pathname and determining the number of file descriptors.


    Pathnames

    Many programs need to allocate storage for a pathname. Typically, the storage has been allocated at compile time, and various magic numbersfew of which are the correct valuehave been used by different programs as the array size: 256, 512, 1024, or the standard I/O constant BUFSIZ. The 4.3BSD constant MAXPATHLEN in the header <sys/param.h> is the correct value, but many 4.3BSD applications didn't use it.

    POSIX.1 tries to help with the PATH_MAX value, but if this value is indeterminate, we're still out of luck. Figure 2.15 shows a function that we'll use throughout this text to allocate storage dynamically for a pathname.


    Figure 2.15. Dynamically allocate space for a pathname

    #include "apue.h"
    #include <errno.h>
    #include <limits.h>
    #ifdef PATH_MAX
    static int pathmax = PATH_MAX;
    #else
    static int pathmax = 0;
    #endif
    #define SUSV3 200112L
    static long posix_version = 0;
    /* If PATH_MAX is indeterminate, no guarantee this is adequate */
    #define PATH_MAX_GUESS 1024
    char *
    path_alloc(int *sizep) /* also return allocated size, if nonnull */
    {
    char *ptr;
    int size;
    if (posix_version == 0)
    posix_version = sysconf(_SC_VERSION);
    if (pathmax == 0) { /* first time through */
    errno = 0;
    if ((pathmax = pathconf("/", _PC_PATH_MAX)) < 0) {
    if (errno == 0)
    pathmax = PATH_MAX_GUESS; /* it's indeterminate */
    else
    err_sys("pathconf error for _PC_PATH_MAX");
    } else {
    pathmax++; /* add one since it's relative to root */
    }
    }
    if (posix_version < SUSV3)
    size = pathmax + 1;
    else
    size = pathmax;
    if ((ptr = malloc(size)) == NULL)
    err_sys("malloc error for pathname");
    if (sizep != NULL)
    *sizep = size;
    return(ptr);
    }

    If the constant PATH_MAX is defined in <limits.h>, then we're all set. If it's not, we need to call pathconf. The value returned by pathconf is the maximum size of a relative pathname when the first argument is the working directory, so we specify the root as the first argument and add 1 to the result. If pathconf indicates that PATH_MAX is indeterminate, we have to punt and just guess a value.

    Standards prior to SUSv3 were unclear as to whether or not PATH_MAX included a null byte at the end of the pathname. If the operating system implementation conforms to one of these prior versions, we need to add 1 to the amount of memory we allocate for a pathname, just to be on the safe side.

    The correct way to handle the case of an indeterminate result depends on how the allocated space is being used. If we were allocating space for a call to getcwd, for exampleto return the absolute pathname of the current working directory; see Section 4.22and if the allocated space is too small, an error is returned and errno is set to ERANGE. We could then increase the allocated space by calling realloc (see Section 7.8 and Exercise 4.16) and try again. We could keep doing this until the call to getcwd succeeded.


    Maximum Number of Open Files

    A common sequence of code in a daemon processa process that runs in the background, not connected to a terminalis one that closes all open files. Some programs have the following code sequence, assuming the constant NOFILE was defined in the <sys/param.h> header:

    #include <sys/param.h>
    for (i = 0; i < NOFILE; i++)
    close(i);

    Other programs use the constant _NFILE that some versions of <stdio.h> provide as the upper limit. Some hard code the upper limit as 20.

    We would hope to use the POSIX.1 value OPEN_MAX to determine this value portably, but if the value is indeterminate, we still have a problem. If we wrote the following and if OPEN_MAX was indeterminate, the loop would never execute, since sysconf would return -1:

    #include <unistd.h>
    for (i = 0; i < sysconf(_SC_OPEN_MAX); i++)
    close(i);

    Our best option in this case is just to close all descriptors up to some arbitrary limit, say 256. As with our pathname example, this is not guaranteed to work for all cases, but it's the best we can do. We show this technique in Figure 2.16.


    Figure 2.16. Determine the number of file descriptors

    #include "apue.h"
    #include <errno.h>
    #include <limits.h>
    #ifdef OPEN_MAX
    static long openmax = OPEN_MAX;
    #else
    static long openmax = 0;
    #endif
    /*
    * If OPEN_MAX is indeterminate, we're not
    * guaranteed that this is adequate.
    */
    #define OPEN_MAX_GUESS 256
    long
    open_max(void)
    {
    if (openmax == 0) { /* first time through */
    errno = 0;
    if ((openmax = sysconf(_SC_OPEN_MAX)) < 0) {
    if (errno == 0)
    openmax = OPEN_MAX_GUESS; /* it's indeterminate */
    else
    err_sys("sysconf error for _SC_OPEN_MAX");
    }
    }
    return(openmax);
    }

    Section 3.12) does return a specific error when OPEN_MAX is exceeded, but duplicating a descriptor a couple of hundred times is an extreme way to determine this value.

    Some implementations will return LONG_MAX for limits values that are effectively unlimited. Such is the case with the Linux limit for ATEXIT_MAX (see Section 7.11). It can be used to return the maximum number of descriptors that a process can have open. With it, we can detect that there is no configured upper bound to the number of open files our processes can open, so we can avoid this problem.

    The OPEN_MAX value is called runtime invariant by POSIX, meaning that its value should not change during the lifetime of a process. But on systems that support the XSI extensions, we can call the setrlimit(2) function (Section 7.11) to change this value for a running process. (This value can also be changed from the C shell with the limit command, and from the Bourne, Bourne-again, and Korn shells with the ulimit command.) If our system supports this functionality, we could change the function in


      / 369