/* * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * The contents of this file constitute Original Code as defined in and * are subject to the Apple Public Source License Version 1.1 (the * "License"). You may not use this file except in compliance with the * License. Please obtain a copy of the License at * http://www.apple.com/publicsource and read it before using this file. * * This Original Code and all software distributed under the License are * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* * macNC.c * - macNC boot server * - supports netboot clients by: * + allocating IP addresses * + locating/creating disk image files * + creating/providing AFP login */ /* * Modification History: * * December 2, 1997 Dieter Siegmund (dieter@apple.com) * - created * February 1, 1999 Dieter Siegmund (dieter@apple.com) * - create sharepoints at init time (and anytime we get a SIGHUP) * and ensure permissions are correct * November 2, 2000 Dieter Siegmund (dieter@apple.com) * - removed code that creates sharepoints */ #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import "dhcp.h" #import "netinfo.h" #import "rfc_options.h" #import "subnetDescr.h" #import "interfaces.h" #import "bootpd.h" #import "macnc_options.h" #import "macNC.h" #import "host_identifier.h" #import "NICache.h" #import "hfsvols.h" #import "nbsp.h" #import "AFPUsers.h" #import "NetBootServer.h" #define AFP_USERS_MAX 50 /* external functions */ char * ether_ntoa(struct ether_addr *e); /* globals */ gid_t netboot_gid; int afp_users_max = AFP_USERS_MAX; /* local defines/variables */ #define NIPROP__CREATOR "_creator" #define MAX_RETRY 5 /* * Define: SHADOW_SIZE_SAME * Meaning: * Make the shadow size file exactly the same size as the file * being shadowed (default behaviour required by NetBoot in Mac OS 8.5). */ #define SHADOW_SIZE_SAME 0 #define SHADOW_SIZE_DEFAULT 48 static nbspList_t S_sharepoints = NULL; static boolean_t S_disk_space_warned = FALSE; static boolean_t S_init_done = FALSE; static u_long S_shadow_size_meg = SHADOW_SIZE_DEFAULT; /* strings retrieved from the configuration directory: */ static ni_name S_client_image_dir = "Clients"; static ni_name S_default_bootfile = "Mac OS ROM"; static ni_name S_images_dir = "Images"; static ni_name S_private_image_name = "Applications HD.img"; static ni_name S_shadow_name = "Shadow"; static nbspEntry_t * S_private_image_volume = NULL; static ni_name S_shared_image_name = "NetBoot HD.img"; static nbspEntry_t * S_shared_image_volume = NULL; static PropList_t S_config_netboot; static __inline__ void S_timestamp(char * msg) { if (verbose) timestamp_syslog(msg); } static __inline__ boolean_t S_gid_taken(ni_entrylist * id_list, gid_t gid) { int i; for (i = 0; i < id_list->niel_len; i++) { ni_namelist * nl_p = id_list->niel_val[i].names; gid_t group_id; if (nl_p == NULL || nl_p->ninl_len == 0) continue; group_id = strtoul(nl_p->ninl_val[0], NULL, NULL); if (group_id == gid) return (TRUE); } return (FALSE); } static boolean_t S_create_netboot_group(gid_t preferred_gid, gid_t * actual_gid) { ni_id dir; ni_entrylist id_list; ni_proplist pl; boolean_t ret = FALSE; ni_status status; gid_t scan; *actual_gid = NULL; NI_INIT(&id_list); NI_INIT(&pl); status = ni_pathsearch(NIDomain_handle(ni_local), &dir, NIDIR_GROUPS); if (status != NI_OK) { syslog(LOG_INFO, "macNC: ni_pathsearch '%s' failed, %s", NIDIR_GROUPS, ni_error(status)); return (FALSE); } status = ni_list(NIDomain_handle(ni_local), &dir, NIPROP_GID, &id_list); if (status != NI_OK) { syslog(LOG_INFO, "macNC: ni_list '%s' failed, %s", NIDIR_GROUPS, ni_error(status)); return (FALSE); } ni_set_prop(&pl, NIPROP_NAME, NETBOOT_GROUP, NULL); ni_set_prop(&pl, NIPROP_PASSWD, "*", NULL); for (scan = preferred_gid; TRUE; scan++) { char buf[64]; ni_id child; if (S_gid_taken(&id_list, scan)) { continue; } snprintf(buf, sizeof(buf), "%d", scan); ni_set_prop(&pl, NIPROP_GID, buf, NULL); { int i; for (i = 0; i < MAX_RETRY; i++) { status = ni_create(NIDomain_handle(ni_local), &dir, pl, &child, NI_INDEX_NULL); if (status == NI_STALE) { ni_self(NIDomain_handle(ni_local), &dir); continue; } *actual_gid = scan; ret = TRUE; goto done; } } if (status != NI_OK) { syslog(LOG_INFO, "macNC: create " NETBOOT_GROUP " group failed, %s", ni_error(status)); goto done; } } done: ni_proplist_free(&pl); ni_entrylist_free(&id_list); return (ret); } static void S_read_config() { ni_namelist * nl_p; S_shadow_size_meg = SHADOW_SIZE_DEFAULT; afp_users_max = AFP_USERS_MAX; if (PropList_read(&S_config_netboot) == TRUE) { nl_p = PropList_lookup(&S_config_netboot, CFGPROP_SHADOW_SIZE_MEG); if (nl_p && nl_p->ninl_len) { S_shadow_size_meg = strtol(nl_p->ninl_val[0], 0, 0); } nl_p = PropList_lookup(&S_config_netboot, CFGPROP_AFP_USERS_MAX); if (nl_p && nl_p->ninl_len) { afp_users_max = strtol(nl_p->ninl_val[0], 0, 0); } } if (S_shadow_size_meg == SHADOW_SIZE_SAME) { syslog(LOG_INFO, "macNC: shadow file size will be set to image file size"); } else { syslog(LOG_INFO, "macNC: shadow file size will be set to %d megabytes", S_shadow_size_meg); } return; } static nbspEntry_t * S_find_images(nbspList_t list) { nbspEntry_t * images = NULL; int i; for (i = 0; i < nbspList_count(list); i++) { nbspEntry_t * entry = nbspList_element(list, i); char path[PATH_MAX]; struct stat sb; snprintf(path, sizeof(path), "%s/%s/%s", entry->path, S_images_dir, S_shared_image_name); if (stat(path, &sb) < 0) { continue; } snprintf(path, sizeof(path), "%s/%s/%s", entry->path, S_images_dir, S_default_bootfile); if (stat(path, &sb) < 0) { continue; } images = entry; break; } return (images); } boolean_t S_set_sharepoint_permissions(nbspList_t list, uid_t user, gid_t group) { boolean_t ret = TRUE; int i; for (i = 0; i < nbspList_count(list); i++) { nbspEntry_t * entry = nbspList_element(list, i); /* * Verify permissions/ownership * We restrict write access to root only, * the Mac NC Group gets read/search, and others * get search only. Others need search because * clients use TFTP to get the boot file that might be * on this volume; tftpd runs as user nobody. */ if (chown(entry->path, user, group) < 0 || chmod(entry->path, 0751) < 0) { syslog(LOG_INFO, "macNC: chmod/chown '%s' failed: %m", entry->path); ret = FALSE; } } return (ret); } /* * Function: S_cfg_init * * Purpose: * This function does all of the variable initialization needed by the * boot server. It can be called multiple times if necessary. */ static boolean_t S_cfg_init() { nbspList_t new_list; syslog(LOG_INFO, "macNC: re-reading configuration"); S_read_config(); /* get the list of sharepoints */ new_list = nbspList_init(); if (new_list) { if (S_sharepoints) nbspList_free(&S_sharepoints); S_sharepoints = new_list; S_shared_image_volume = S_find_images(S_sharepoints); if (S_shared_image_volume == NULL) { syslog(LOG_INFO, "macNC: NetBoot images not found"); return (FALSE); } S_private_image_volume = S_shared_image_volume; } else if (S_sharepoints == NULL) { return (FALSE); } if (S_set_sharepoint_permissions(S_sharepoints, 0, netboot_gid) == FALSE) { return (FALSE); } if (debug) { nbspList_print(S_sharepoints); } return (TRUE); } /* * Function: S_init * * Purpose: * Initialize state for dealing with macNC's: * Returns: * TRUE if success, FALSE if failure */ static boolean_t S_init() { struct group * group_ent_p; struct timeval tv; /* one-time initialization */ if (S_init_done) return (TRUE); if (ni_local == NULL) { syslog(LOG_INFO, "macNC: local netinfo domain not yet open"); return (FALSE); } PropList_init(&S_config_netboot, "/config/NetBootServer"); group_ent_p = getgrnam(NETBOOT_GROUP); if (group_ent_p == NULL) { #define NETBOOT_GID 120 if (S_create_netboot_group(NETBOOT_GID, &netboot_gid) == FALSE) { return (FALSE); } } else { netboot_gid = group_ent_p->gr_gid; } /* read the configuration directory */ if (S_cfg_init() == FALSE) { return (FALSE); } /* use microseconds for the random seed: password is a random number */ gettimeofday(&tv, 0); srandom(tv.tv_usec); /* one-time initialization */ S_init_done = TRUE; return (TRUE); } /* * Function: macNC_init * * Purpose: * Called from bootp if we received a SIGHUP. */ boolean_t macNC_init() { S_disk_space_warned = FALSE; if (S_init_done == TRUE) (void)S_cfg_init(); /* subsequent initialization */ else if (S_init() == FALSE) { /* one-time initialization */ syslog(LOG_INFO, "macNC: NetBoot service turned off"); return (FALSE); } return (TRUE); } /* * Function: S_set_uid_gid * * Purpose: * Given a path to a file, make the owner of both the * enclosing directory and the file itself to user/group uid/gid. */ static int S_set_uid_gid(u_char * file, uid_t uid, gid_t gid) { u_char dir[PATH_MAX]; u_char * last_slash = strrchr(file, '/'); if (file[0] != '/' || last_slash == NULL) { if (debug) printf("path '%s' is not valid\n", file); return (-1); } strncpy(dir, file, last_slash - file); dir[last_slash - file] = '\0'; if (chown(dir, uid, gid) == -1) return (-1); if (chown(file, uid, gid) == -1) return (-1); return (0); } /** ** Other local utility routines: **/ /* * Function: S_get_client_info * * Purpose: * Retrieve the macNC client information from the given packet. * First try to parse the dhcp options, then look for the client * version tag and client info tag. The client info tag will * contain "Apple MacNC". * * Returns: * TRUE and client_version if client version is present in packet * FALSE otherwise */ boolean_t macNC_get_client_info(struct dhcp * pkt, int pkt_size, dhcpol_t * options, u_int * client_version) { /* get the client version info - if not present, not an NC */ void * client_id; int opt_len; void * vers; vers = dhcpol_find(options, macNCtag_client_version_e, &opt_len, NULL); if (vers == NULL) return (FALSE); client_id = dhcpol_find(options, macNCtag_client_info_e, &opt_len, NULL); if (client_id == NULL) return (FALSE); if (opt_len != strlen(MACNC_CLIENT_INFO) || bcmp(client_id, MACNC_CLIENT_INFO, opt_len)) { return (FALSE); } if (client_version) *client_version = ntohl(*((unsigned long *)vers)); return (TRUE); } static __inline__ boolean_t S_make_finder_info(u_char * shadow_path, u_char * real_path) { return (hfs_copy_finder_info(shadow_path, real_path)); } /* * Function: S_get_volpath * * Purpose: * Format a volume pathname given a volume, directory and file name. */ static void S_get_volpath(u_char * path, nbspEntry_t * entry, u_char * dir, u_char * file) { snprintf(path, PATH_MAX, "%s%s%s%s%s", entry->path, (dir && *dir) ? "/" : "", (dir && *dir) ? (char *)dir : "", (file && *file) ? "/" : "", (file && *file) ? (char *)file : ""); return; } /* * Function: S_create_volume_dir * * Purpose: * Create the given directory path on the given volume. */ static boolean_t S_create_volume_dir(nbspEntry_t * entry, u_char * dirname) { u_char path[PATH_MAX]; S_get_volpath(path, entry, dirname, NULL); if (create_path(path, 0755) < 0) { syslog(LOG_INFO, "macNC: create_volume_dir: create_path(%s)" " failed, %m", path); return (FALSE); } return (TRUE); } /* * Function: S_create_shadow_file * * Purpose: * Create a new empty file with the given size and attributes * from another file. */ #define SHADOW_FILE_PERMS 0700 static boolean_t S_create_shadow_file(u_char * shadow_path, u_char * real_path, uid_t uid, gid_t gid, unsigned long long size) { int fd; S_set_uid_gid(shadow_path, 0, 0); fd = open(shadow_path, /* O_NO_MFS | */ O_CREAT | O_TRUNC | O_WRONLY, SHADOW_FILE_PERMS); if (fd < 0) { syslog(LOG_INFO, "macNC: couldn't create file '%s': %m", shadow_path); return (FALSE); } if (hfs_set_file_size(fd, size)) { syslog(LOG_INFO, "macNC: hfs_set_file_size '%s' failed: %m", shadow_path); goto err; } if (S_make_finder_info(shadow_path, real_path) == FALSE) goto err; fchmod(fd, SHADOW_FILE_PERMS); close(fd); /* correct the owner of the path */ if (S_set_uid_gid(shadow_path, uid, gid)) { syslog(LOG_INFO, "macNC: setuidgid '%s' to %ld,%ld failed: %m", shadow_path, uid, gid); return (FALSE); } return (TRUE); err: close(fd); return (FALSE); } static boolean_t S_add_afppath_option(struct in_addr servip, dhcpoa_t * options, nbspEntry_t * entry, u_char * dir, u_char * file, int tag) { u_char buf[DHCP_OPTION_SIZE_MAX]; u_char err[256]; int len; u_char path[PATH_MAX]; if (dir && *dir) snprintf(path, sizeof(path), "%s/%s", dir, file); else { snprintf(path, sizeof(path), "%s", file); } len = sizeof(buf); if (macNCopt_encodeAFPPath(servip, AFP_PORT_NUMBER, entry->name, AFP_DIRID_NULL, AFP_PATHTYPE_LONG, path, '/', buf, &len, err) == FALSE) { syslog(LOG_INFO, "macNC: couldn't encode %s:%s, %s", entry->name, path, err); return (FALSE); } if (dhcpoa_add(options, tag, len, buf) != dhcpoa_success_e) { syslog(LOG_INFO, "macNC: couldn't add option %d failed: %s", tag, dhcpoa_err(options)); return (FALSE); } return (TRUE); } /* * Function: S_stat_path_vol_file * * Purpose: * Return the stat structure for the given volume/dir/file. */ static __inline__ int S_stat_path_vol_file(u_char * path, nbspEntry_t * entry, u_char * dir, u_char * file, struct stat * sb_p) { S_get_volpath(path, entry, dir, file); return (stat(path, sb_p)); } static __inline__ boolean_t S_stat_shared(u_char * shared_path, struct stat * sb_p) { S_get_volpath(shared_path, S_shared_image_volume, S_images_dir, S_shared_image_name); if (stat(shared_path, sb_p) != 0) return (FALSE); return (TRUE); } static __inline__ boolean_t S_stat_private(u_char * private_path, struct stat * sb_p) { S_get_volpath(private_path, S_private_image_volume, S_images_dir, S_private_image_name); if (stat(private_path, sb_p) != 0) return (FALSE); return (TRUE); } static boolean_t S_freespace(u_char * path, unsigned long long * size) { struct statfs fsb; if (statfs(path, &fsb) != 0) { syslog(LOG_INFO, "macNC: statfs on '%s' failed %m", path); return (FALSE); } *size = ((unsigned long long)fsb.f_bavail) * ((unsigned long long)fsb.f_bsize); if (debug) printf("%s %lu x %lu = %qu bytes\n", path, fsb.f_bavail, fsb.f_bsize, *size); return (TRUE); } static nbspEntry_t * S_find_volume_with_space(unsigned long long needspace, int def_vol_index) { unsigned long long freespace; int i; nbspEntry_t * entry = NULL; u_char path[PATH_MAX]; int vol_index; for (i = 0, vol_index = def_vol_index; i < nbspList_count(S_sharepoints); i++) { nbspEntry_t * shp = nbspList_element(S_sharepoints, vol_index); S_get_volpath(path, shp, NULL, NULL); if (S_freespace(path, &freespace) == TRUE) { #define SLOP_SPACE_BYTES (20 * 1024 * 1024) /* make sure there's some space left on the volume */ if (freespace >= (needspace + SLOP_SPACE_BYTES)) { entry = shp; if (debug) printf("selected volume %s\n", entry->name); break; /* out of for */ } } vol_index = (vol_index + 1) % nbspList_count(S_sharepoints); } return (entry); } static boolean_t S_remove_shadow(u_char * shadow_path, nbspEntry_t * entry, u_char * dir) { u_char path[PATH_MAX]; /* remove the shadow file */ S_set_uid_gid(shadow_path, 0, 0); unlink(shadow_path); S_get_volpath(path, entry, dir, NULL); /* and its directory */ if (rmdir(path)) { u_char new_path[PATH_MAX]; if (debug) perror(path); S_get_volpath(new_path, entry, "Delete Me", NULL); /* couldn't delete it, try to rename it */ if (rename(path, new_path)) { return (FALSE); } } return (TRUE); } /* * Function: S_add_image_options * * Purpose: * Create/initialize image for client, format the paths into the * response options. */ static boolean_t S_add_image_options(uid_t uid, gid_t gid, struct in_addr servip, dhcpoa_t * options, int host_number, u_char * afp_hostname) { int def_vol_index; int i; u_char nc_images_dir[PATH_MAX]; nbspEntry_t * nc_volume = NULL; u_char path[PATH_MAX]; struct stat statb; int vol_index; snprintf(nc_images_dir, sizeof(nc_images_dir), "%s/%s", S_client_image_dir, afp_hostname); /* attempt to round-robin images across multiple volumes */ def_vol_index = (host_number - 1) % nbspList_count(S_sharepoints); /* check all volumes for a client image directory starting at default */ nc_volume = NULL; for (i = 0, vol_index = def_vol_index; i < nbspList_count(S_sharepoints); i++) { nbspEntry_t * entry = nbspList_element(S_sharepoints, vol_index); if (S_stat_path_vol_file(path, entry, nc_images_dir, NULL, &statb) == 0) { nc_volume = entry; break; } vol_index = (vol_index + 1) % nbspList_count(S_sharepoints); } /* if the client has its own private copy of the image file, use it */ if (nc_volume != NULL && S_stat_path_vol_file(path, nc_volume, nc_images_dir, S_shared_image_name, &statb) == 0) { if (S_set_uid_gid(path, uid, gid)) { syslog(LOG_INFO, "macNC: couldn't set permissions on path %s: %m", path); return (FALSE); } else chflags(path, 0); /* make shared image writable */ if (S_add_afppath_option(servip, options, nc_volume, nc_images_dir, S_shared_image_name, macNCtag_shared_system_file_e) == FALSE) { return (FALSE); } /* does the client have its own Private image? */ if (S_stat_path_vol_file(path, nc_volume, nc_images_dir, S_private_image_name, &statb) == 0) { /* * We use macNCtag_page_file_e instead of * macNCtag_private_system_file_e as you would expect. * The reason is that the client ROM software assumes * that the private_system_file is read-only. It also * assumes that page_file is read-write. Since we don't * use page_file for anything else, we use that instead. * This is a hack/workaround. */ if (S_add_afppath_option(servip, options, nc_volume, nc_images_dir, S_private_image_name, macNCtag_page_file_e) == FALSE){ return (FALSE); } if (S_set_uid_gid(path, uid, gid)) { syslog(LOG_INFO, "macNC: couldn't set permissions on path %s: %m", path); return (FALSE); } else chflags(path, 0); /* make private image writable */ } } else { /* client gets shadow file(s) */ unsigned long long needspace; u_char private_path[PATH_MAX]; struct stat sb_shared; struct stat sb_private; u_char shadow_path[PATH_MAX]; u_char shared_path[PATH_MAX]; /* make sure that the shared system image exists */ if (S_stat_shared(shared_path, &sb_shared) == FALSE) { syslog(LOG_INFO, "macNC: '%s' does not exist", shared_path); return (FALSE); } chflags(shared_path, UF_IMMUTABLE); /* lock the share image file */ /* add the shared system image option */ if (S_add_afppath_option(servip, options, S_shared_image_volume, S_images_dir, S_shared_image_name, macNCtag_shared_system_file_e) == FALSE) return (FALSE); if (S_stat_private(private_path, &sb_private)) { if (S_add_afppath_option(servip, options, S_private_image_volume, S_images_dir, S_private_image_name, macNCtag_private_system_file_e) == FALSE) return (FALSE); /* lock the private image file */ chflags(private_path, UF_IMMUTABLE); } #define ONE_MEG (1024UL * 1024UL) needspace = (S_shadow_size_meg == SHADOW_SIZE_SAME) ? sb_shared.st_size : ((unsigned long long)S_shadow_size_meg) * ONE_MEG; if (nc_volume != NULL) { struct stat sb_shadow; boolean_t set_file_size = FALSE; boolean_t set_owner_perms = FALSE; S_get_volpath(shadow_path, nc_volume, nc_images_dir, S_shadow_name); if (stat(shadow_path, &sb_shadow) == 0) { /* shadow exists */ S_timestamp("shadow file exists"); if (debug) printf("shadow %qu need %qu\n", sb_shadow.st_size, needspace); if (sb_shadow.st_uid != uid || (sb_shadow.st_mode & ACCESSPERMS) != SHADOW_FILE_PERMS) set_owner_perms = TRUE; if (sb_shadow.st_size != needspace) { set_file_size = TRUE; if (sb_shadow.st_size < needspace) { unsigned long long difference; unsigned long long freespace = 0; S_timestamp("shadow file needs to be grown"); /* check for enough space */ (void)S_freespace(shadow_path, &freespace); difference = (needspace - sb_shadow.st_size); if (freespace < difference) { syslog(LOG_INFO, "macNC: device full, " "attempting to relocate %s", shadow_path); /* blow away the shadow */ if (S_remove_shadow(shadow_path, nc_volume, nc_images_dir) == FALSE) { syslog(LOG_INFO, "macNC: couldn't remove" " shadow %s, %m", shadow_path); return (FALSE); } /* start fresh */ nc_volume = NULL; } } } } else { /* start fresh */ if (S_remove_shadow(shadow_path, nc_volume, nc_images_dir) == FALSE) { syslog(LOG_INFO, "macNC: couldn't remove" " shadow %s, %m", shadow_path); return (FALSE); } nc_volume = NULL; } if (nc_volume) { if (set_file_size) { S_timestamp("setting shadow file size"); if (S_create_shadow_file(shadow_path, shared_path, uid, gid, needspace) == FALSE) { syslog(LOG_INFO, "macNC: couldn't create %s, %m", shadow_path); return (FALSE); } S_timestamp("shadow file size set"); } else if (set_owner_perms) { S_timestamp("setting shadow file perms/owner"); chmod(shadow_path, SHADOW_FILE_PERMS); S_set_uid_gid(shadow_path, uid, gid); S_timestamp("shadow file perms/owner set"); } } } if (nc_volume == NULL) { /* locate the client's image dir */ nc_volume = S_find_volume_with_space(needspace, def_vol_index); if (nc_volume == NULL) { if (S_disk_space_warned == FALSE) syslog(LOG_INFO, "macNC: can't create client image: " "OUT OF DISK SPACE"); S_disk_space_warned = TRUE; /* don't keep complaining */ return (FALSE); } S_get_volpath(shadow_path, nc_volume, nc_images_dir, S_shadow_name); S_disk_space_warned = FALSE; if (S_create_volume_dir(nc_volume, nc_images_dir) == FALSE) return (FALSE); if (S_create_shadow_file(shadow_path, shared_path, uid, gid, needspace) == FALSE) { syslog(LOG_INFO, "macNC: couldn't create %s, %m", shadow_path); return (FALSE); } } /* add the shadow file option */ if (S_add_afppath_option(servip, options, nc_volume, nc_images_dir, S_shadow_name, macNCtag_shared_system_shadow_file_e) == FALSE) { return (FALSE); } } return (TRUE); } boolean_t macNC_allocate(struct dhcp * reply, u_char * hostname, struct in_addr servip, int host_number, dhcpoa_t * options, uid_t uid, u_char * afp_user, u_char * passwd) { if (bootp_add_bootfile(NULL, hostname, S_default_bootfile, reply->dp_file) == FALSE) { syslog(LOG_INFO, "macNC: bootp_add_bootfile failed"); return (FALSE); } if (dhcpoa_add(options, macNCtag_user_name_e, strlen(afp_user), afp_user) != dhcpoa_success_e) { if (!quiet) { syslog(LOG_INFO, "macNC: afp user name option add %s failed, %s", afp_user, dhcpoa_err(options)); } return (FALSE); } /* add the Mac OS machine name option */ if (dhcpoa_add(options, macNCtag_MacOS_machine_name_e, strlen(hostname), hostname) != dhcpoa_success_e) { if (!quiet) { syslog(LOG_INFO, "macNC: machine name option add client %s failed, %s", hostname, dhcpoa_err(options)); } return (FALSE); } { u_char buf[16]; int buf_len = sizeof(buf); if (macNCopt_str_to_type(passwd, macNCtype_afp_password_e, buf, &buf_len, NULL) == FALSE || dhcpoa_add(options, macNCtag_password_e, buf_len, buf) != dhcpoa_success_e) { if (!quiet) { syslog(LOG_INFO, "macNC: failed add afp password for %d", host_number); } return (FALSE); } } if (S_add_image_options(uid, netboot_gid, servip, options, host_number, hostname) == FALSE) { if (!quiet) { syslog(LOG_INFO, "macNC: S_add_image_options for %s failed", afp_user); } } return (TRUE); }