]> git.saurik.com Git - apple/configd.git/commitdiff
configd-24.tar.gz mac-os-x-100 v24
authorApple <opensource@apple.com>
Wed, 11 Apr 2001 13:07:37 +0000 (13:07 +0000)
committerApple <opensource@apple.com>
Wed, 11 Apr 2001 13:07:37 +0000 (13:07 +0000)
136 files changed:
Makefile [new file with mode: 0644]
Makefile.postamble [new file with mode: 0644]
Makefile.preamble [new file with mode: 0644]
PB.project [new file with mode: 0644]
SystemConfiguration.fproj/CustomInfo.plist [new file with mode: 0644]
SystemConfiguration.fproj/Makefile [new file with mode: 0644]
SystemConfiguration.fproj/Makefile.postamble [new file with mode: 0644]
SystemConfiguration.fproj/Makefile.preamble [new file with mode: 0644]
SystemConfiguration.fproj/PB.project [new file with mode: 0644]
SystemConfiguration.fproj/SCD.c [new file with mode: 0644]
SystemConfiguration.fproj/SCD.h [new file with mode: 0644]
SystemConfiguration.fproj/SCDAdd.c [new file with mode: 0644]
SystemConfiguration.fproj/SCDAddSession.c [new file with mode: 0644]
SystemConfiguration.fproj/SCDClose.c [new file with mode: 0644]
SystemConfiguration.fproj/SCDConsoleUser.c [new file with mode: 0644]
SystemConfiguration.fproj/SCDConsoleUser.h [new file with mode: 0644]
SystemConfiguration.fproj/SCDGet.c [new file with mode: 0644]
SystemConfiguration.fproj/SCDHandle.c [new file with mode: 0644]
SystemConfiguration.fproj/SCDHostName.c [new file with mode: 0644]
SystemConfiguration.fproj/SCDHostName.h [new file with mode: 0644]
SystemConfiguration.fproj/SCDKeys.c [new file with mode: 0644]
SystemConfiguration.fproj/SCDKeys.h [new file with mode: 0644]
SystemConfiguration.fproj/SCDList.c [new file with mode: 0644]
SystemConfiguration.fproj/SCDLock.c [new file with mode: 0644]
SystemConfiguration.fproj/SCDNotifierAdd.c [new file with mode: 0644]
SystemConfiguration.fproj/SCDNotifierCancel.c [new file with mode: 0644]
SystemConfiguration.fproj/SCDNotifierGetChanges.c [new file with mode: 0644]
SystemConfiguration.fproj/SCDNotifierInformViaCallback.c [new file with mode: 0644]
SystemConfiguration.fproj/SCDNotifierInformViaFD.c [new file with mode: 0644]
SystemConfiguration.fproj/SCDNotifierInformViaMachPort.c [new file with mode: 0644]
SystemConfiguration.fproj/SCDNotifierInformViaSignal.c [new file with mode: 0644]
SystemConfiguration.fproj/SCDNotifierList.c [new file with mode: 0644]
SystemConfiguration.fproj/SCDNotifierRemove.c [new file with mode: 0644]
SystemConfiguration.fproj/SCDNotifierWait.c [new file with mode: 0644]
SystemConfiguration.fproj/SCDOpen.c [new file with mode: 0644]
SystemConfiguration.fproj/SCDPrivate.c [new file with mode: 0644]
SystemConfiguration.fproj/SCDPrivate.h [new file with mode: 0644]
SystemConfiguration.fproj/SCDRemove.c [new file with mode: 0644]
SystemConfiguration.fproj/SCDSet.c [new file with mode: 0644]
SystemConfiguration.fproj/SCDSnapshot.c [new file with mode: 0644]
SystemConfiguration.fproj/SCDTouch.c [new file with mode: 0644]
SystemConfiguration.fproj/SCDUnlock.c [new file with mode: 0644]
SystemConfiguration.fproj/SCNetwork.c [new file with mode: 0644]
SystemConfiguration.fproj/SCNetwork.h [new file with mode: 0644]
SystemConfiguration.fproj/SCP.c [new file with mode: 0644]
SystemConfiguration.fproj/SCP.h [new file with mode: 0644]
SystemConfiguration.fproj/SCPAdd.c [new file with mode: 0644]
SystemConfiguration.fproj/SCPApply.c [new file with mode: 0644]
SystemConfiguration.fproj/SCPClose.c [new file with mode: 0644]
SystemConfiguration.fproj/SCPCommit.c [new file with mode: 0644]
SystemConfiguration.fproj/SCPGet.c [new file with mode: 0644]
SystemConfiguration.fproj/SCPList.c [new file with mode: 0644]
SystemConfiguration.fproj/SCPLock.c [new file with mode: 0644]
SystemConfiguration.fproj/SCPOpen.c [new file with mode: 0644]
SystemConfiguration.fproj/SCPPath.c [new file with mode: 0644]
SystemConfiguration.fproj/SCPPath.h [new file with mode: 0644]
SystemConfiguration.fproj/SCPPrivate.h [new file with mode: 0644]
SystemConfiguration.fproj/SCPRemove.c [new file with mode: 0644]
SystemConfiguration.fproj/SCPSet.c [new file with mode: 0644]
SystemConfiguration.fproj/SCPUnlock.c [new file with mode: 0644]
SystemConfiguration.fproj/SystemConfiguration.h [new file with mode: 0644]
SystemConfiguration.fproj/config.defs [new file with mode: 0644]
SystemConfiguration.fproj/config_types.h [new file with mode: 0644]
SystemConfiguration.fproj/genSCPreferences.c [new file with mode: 0644]
SystemConfiguration.fproj/h.template [new file with mode: 0644]
SystemConfiguration.fproj/m.template [new file with mode: 0644]
SystemConfiguration.fproj/ppp.c [new file with mode: 0644]
SystemConfiguration.fproj/ppp.h [new file with mode: 0644]
SystemConfiguration.fproj/ppp_msg.h [new file with mode: 0644]
configd.tproj/Makefile [new file with mode: 0644]
configd.tproj/Makefile.postamble [new file with mode: 0644]
configd.tproj/Makefile.preamble [new file with mode: 0644]
configd.tproj/PB.project [new file with mode: 0644]
configd.tproj/_SCD.c [new file with mode: 0644]
configd.tproj/_SCD.h [new file with mode: 0644]
configd.tproj/_configadd.c [new file with mode: 0644]
configd.tproj/_configadd_s.c [new file with mode: 0644]
configd.tproj/_configclose.c [new file with mode: 0644]
configd.tproj/_configget.c [new file with mode: 0644]
configd.tproj/_configlist.c [new file with mode: 0644]
configd.tproj/_configlock.c [new file with mode: 0644]
configd.tproj/_configopen.c [new file with mode: 0644]
configd.tproj/_configremove.c [new file with mode: 0644]
configd.tproj/_configset.c [new file with mode: 0644]
configd.tproj/_configtouch.c [new file with mode: 0644]
configd.tproj/_configunlock.c [new file with mode: 0644]
configd.tproj/_notifyadd.c [new file with mode: 0644]
configd.tproj/_notifycancel.c [new file with mode: 0644]
configd.tproj/_notifychanges.c [new file with mode: 0644]
configd.tproj/_notifyremove.c [new file with mode: 0644]
configd.tproj/_notifyviafd.c [new file with mode: 0644]
configd.tproj/_notifyviaport.c [new file with mode: 0644]
configd.tproj/_notifyviasignal.c [new file with mode: 0644]
configd.tproj/_snapshot.c [new file with mode: 0644]
configd.tproj/config.defs [new file with mode: 0644]
configd.tproj/configd.h [new file with mode: 0644]
configd.tproj/configd.m [new file with mode: 0644]
configd.tproj/configd_server.c [new file with mode: 0644]
configd.tproj/configd_server.h [new file with mode: 0644]
configd.tproj/h.template [new file with mode: 0644]
configd.tproj/m.template [new file with mode: 0644]
configd.tproj/notify.c [new file with mode: 0644]
configd.tproj/notify.h [new file with mode: 0644]
configd.tproj/notify_server.c [new file with mode: 0644]
configd.tproj/notify_server.h [new file with mode: 0644]
configd.tproj/plugin_support.c [new file with mode: 0644]
configd.tproj/plugin_support.h [new file with mode: 0644]
configd.tproj/session.c [new file with mode: 0644]
configd.tproj/session.h [new file with mode: 0644]
scselect.tproj/Makefile [new file with mode: 0644]
scselect.tproj/Makefile.postamble [new file with mode: 0644]
scselect.tproj/Makefile.preamble [new file with mode: 0644]
scselect.tproj/PB.project [new file with mode: 0644]
scselect.tproj/h.template [new file with mode: 0644]
scselect.tproj/m.template [new file with mode: 0644]
scselect.tproj/scselect.c [new file with mode: 0644]
scutil.tproj/Makefile [new file with mode: 0644]
scutil.tproj/Makefile.postamble [new file with mode: 0644]
scutil.tproj/Makefile.preamble [new file with mode: 0644]
scutil.tproj/PB.project [new file with mode: 0644]
scutil.tproj/cache.c [new file with mode: 0644]
scutil.tproj/cache.h [new file with mode: 0644]
scutil.tproj/commands.c [new file with mode: 0644]
scutil.tproj/commands.h [new file with mode: 0644]
scutil.tproj/dictionary.c [new file with mode: 0644]
scutil.tproj/dictionary.h [new file with mode: 0644]
scutil.tproj/h.template [new file with mode: 0644]
scutil.tproj/m.template [new file with mode: 0644]
scutil.tproj/notify.c [new file with mode: 0644]
scutil.tproj/notify.h [new file with mode: 0644]
scutil.tproj/scutil.c [new file with mode: 0644]
scutil.tproj/scutil.h [new file with mode: 0644]
scutil.tproj/session.c [new file with mode: 0644]
scutil.tproj/session.h [new file with mode: 0644]
scutil.tproj/tests.c [new file with mode: 0644]
scutil.tproj/tests.h [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..61c7f5a
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,46 @@
+#
+# Generated by the Apple Project Builder.
+#
+# NOTE: Do NOT change this file -- Project Builder maintains it.
+#
+# Put all of your customizations in files called Makefile.preamble
+# and Makefile.postamble (both optional), and Makefile will include them.
+#
+
+NAME = configd
+
+PROJECTVERSION = 2.8
+PROJECT_TYPE = Aggregate
+
+TOOLS = configd.tproj scselect.tproj scutil.tproj
+
+FRAMEWORK_SUBPROJECTS = SystemConfiguration.fproj
+
+OTHERSRCS = Makefile.preamble Makefile Makefile.postamble
+
+MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles
+CODE_GEN_STYLE = DYNAMIC
+MAKEFILE = aggregate.make
+LIBS = 
+DEBUG_LIBS = $(LIBS)
+PROF_LIBS = $(LIBS)
+
+
+
+
+NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc
+WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc
+PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc
+NEXTSTEP_JAVA_COMPILER = /usr/bin/javac
+WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe
+PDO_UNIX_JAVA_COMPILER = $(JDKBINDIR)/javac
+
+include $(MAKEFILEDIR)/platform.make
+
+-include Makefile.preamble
+
+include $(MAKEFILEDIR)/$(MAKEFILE)
+
+-include Makefile.postamble
+
+-include Makefile.dependencies
diff --git a/Makefile.postamble b/Makefile.postamble
new file mode 100644 (file)
index 0000000..b3af842
--- /dev/null
@@ -0,0 +1,100 @@
+###############################################################################
+#  Makefile.postamble
+#  Copyright 1997, Apple Computer, Inc.
+#
+#  Use this makefile, which is imported after all other makefiles, to
+#  override attributes for a project's Makefile environment. This allows you
+#  to take advantage of the environment set up by the other Makefiles.
+#  You can also define custom rules at the end of this file.
+#
+###############################################################################
+#
+# These variables are exported by the standard makefiles and can be
+# used in any customizations you make.  They are *outputs* of
+# the Makefiles and should be used, not set.
+#
+#  PRODUCTS: products to install.  All of these products will be placed in
+#       the directory $(DSTROOT)$(INSTALLDIR)
+#  GLOBAL_RESOURCE_DIR: The directory to which resources are copied.
+#  LOCAL_RESOURCE_DIR: The directory to which localized resources are copied.
+#  OFILE_DIR: Directory into which .o object files are generated.
+#  DERIVED_SRC_DIR: Directory used for all other derived files
+#
+#  ALL_CFLAGS:  flags to pass when compiling .c files
+#  ALL_MFLAGS:  flags to pass when compiling .m files
+#  ALL_CCFLAGS:  flags to pass when compiling .cc, .cxx, and .C files
+#  ALL_MMFLAGS:  flags to pass when compiling .mm, .mxx, and .M files
+#  ALL_PRECOMPFLAGS:  flags to pass when precompiling .h files
+#  ALL_LDFLAGS:  flags to pass when linking object files
+#  ALL_LIBTOOL_FLAGS:  flags to pass when libtooling object files
+#  ALL_PSWFLAGS:  flags to pass when processing .psw and .pswm (pswrap) files
+#  ALL_RPCFLAGS:  flags to pass when processing .rpc (rpcgen) files
+#  ALL_YFLAGS:  flags to pass when processing .y (yacc) files
+#  ALL_LFLAGS:  flags to pass when processing .l (lex) files
+#
+#  NAME: name of application, bundle, subproject, palette, etc.
+#  LANGUAGES: langages in which the project is written (default "English")
+#  English_RESOURCES: localized resources (e.g. nib's, images) of project
+#  GLOBAL_RESOURCES: non-localized resources of project
+#
+#  SRCROOT:  base directory in which to place the new source files
+#  SRCPATH:  relative path from SRCROOT to present subdirectory
+#
+#  INSTALLDIR: Directory the product will be installed into by 'install' target
+#  PUBLIC_HDR_INSTALLDIR: where to install public headers.  Don't forget
+#        to prefix this with DSTROOT when you use it.
+#  PRIVATE_HDR_INSTALLDIR: where to install private headers.  Don't forget
+#       to prefix this with DSTROOT when you use it.
+#
+#  EXECUTABLE_EXT: Executable extension for the platform (i.e. .exe on Windows)
+#
+###############################################################################
+
+# Some compiler flags can be overridden here for certain build situations.
+#
+#    WARNING_CFLAGS:  flag used to set warning level (defaults to -Wmost)
+#    DEBUG_SYMBOLS_CFLAGS:  debug-symbol flag passed to all builds (defaults
+#      to -g)
+#    DEBUG_BUILD_CFLAGS:  flags passed during debug builds (defaults to -DDEBUG)
+#    OPTIMIZE_BUILD_CFLAGS:  flags passed during optimized builds (defaults
+#      to -O)
+#    PROFILE_BUILD_CFLAGS:  flags passed during profile builds (defaults
+#      to -pg -DPROFILE)
+#    LOCAL_DIR_INCLUDE_DIRECTIVE:  flag used to add current directory to
+#      the include path (defaults to -I.)
+#    DEBUG_BUILD_LDFLAGS, OPTIMIZE_BUILD_LDFLAGS, PROFILE_BUILD_LDFLAGS: flags
+#      passed to ld/libtool (defaults to nothing)
+
+
+# Library and Framework projects only:
+#    INSTALL_NAME_DIRECTIVE:  This directive ensures that executables linked
+#      against the framework will run against the correct version even if
+#      the current version of the framework changes.  You may override this
+#      to "" as an alternative to using the DYLD_LIBRARY_PATH during your
+#      development cycle, but be sure to restore it before installing.
+
+
+# Ownership and permissions of files installed by 'install' target
+
+#INSTALL_AS_USER = root
+       # User/group ownership
+#INSTALL_AS_GROUP = wheel
+       # (probably want to set both of these)
+#INSTALL_PERMISSIONS =
+       # If set, 'install' chmod's executable to this
+
+
+# Options to strip.  Note: -S strips debugging symbols (executables can be stripped
+# down further with -x or, if they load no bundles, with no options at all).
+
+#STRIPFLAGS = -S
+
+
+#########################################################################
+# Put rules to extend the behavior of the standard Makefiles here.  Include them in
+# the dependency tree via cvariables like AFTER_INSTALL in the Makefile.preamble.
+#
+# You should avoid redefining things like "install" or "app", as they are
+# owned by the top-level Makefile API and no context has been set up for where
+# derived files should go.
+#
diff --git a/Makefile.preamble b/Makefile.preamble
new file mode 100644 (file)
index 0000000..974c0ac
--- /dev/null
@@ -0,0 +1,137 @@
+###############################################################################
+#  Makefile.preamble
+#  Copyright 1997, Apple Computer, Inc.
+#
+#  Use this makefile for configuring the standard application makefiles
+#  associated with ProjectBuilder. It is included before the main makefile.
+#  In Makefile.preamble you set attributes for a project, so they are available
+#  to the project's makefiles.  In contrast, you typically write additional rules or
+#  override built-in behavior in the Makefile.postamble.
+#
+#  Each directory in a project tree (main project plus subprojects) should
+#  have its own Makefile.preamble and Makefile.postamble.
+###############################################################################
+#
+# Before the main makefile is included for this project, you may set:
+#
+#    MAKEFILEDIR: Directory in which to find $(MAKEFILE)
+#    MAKEFILE: Top level mechanism Makefile (e.g., app.make, bundle.make)
+
+# Compiler/linker flags added to the defaults:  The OTHER_* variables will be
+# inherited by all nested sub-projects, but the LOCAL_ versions of the same
+# variables will not.  Put your -I, -D, -U, and -L flags in ProjectBuilder's
+# Build Attributes inspector if at all possible.  To override the default flags
+# that get passed to ${CC} (e.g. change -O to -O2), see Makefile.postamble.  The
+# variables below are *inputs* to the build process and distinct from the override
+# settings done (less often) in the Makefile.postamble.
+#
+#    OTHER_CFLAGS, LOCAL_CFLAGS:  additional flags to pass to the compiler
+#      Note that $(OTHER_CFLAGS) and $(LOCAL_CFLAGS) are used for .h, ...c, .m,
+#      .cc, .cxx, .C, and .M files.  There is no need to respecify the
+#      flags in OTHER_MFLAGS, etc.
+#    OTHER_MFLAGS, LOCAL_MFLAGS:  additional flags for .m files
+#    OTHER_CCFLAGS, LOCAL_CCFLAGS:  additional flags for .cc, .cxx, and ...C files
+#    OTHER_MMFLAGS, LOCAL_MMFLAGS:  additional flags for .mm and .M files
+#    OTHER_PRECOMPFLAGS, LOCAL_PRECOMPFLAGS:  additional flags used when
+#      precompiling header files
+#    OTHER_LDFLAGS, LOCAL_LDFLAGS:  additional flags passed to ld and libtool
+#    OTHER_PSWFLAGS, LOCAL_PSWFLAGS:  additional flags passed to pswrap
+#    OTHER_RPCFLAGS, LOCAL_RPCFLAGS:  additional flags passed to rpcgen
+#    OTHER_YFLAGS, LOCAL_YFLAGS:  additional flags passed to yacc
+#    OTHER_LFLAGS, LOCAL_LFLAGS:  additional flags passed to lex
+
+# These variables provide hooks enabling you to add behavior at almost every
+# stage of the make:
+#
+#    BEFORE_PREBUILD: targets to build before installing headers for a subproject
+#    AFTER_PREBUILD: targets to build after installing headers for a subproject
+#    BEFORE_BUILD_RECURSION: targets to make before building subprojects
+#    BEFORE_BUILD: targets to make before a build, but after subprojects
+#    AFTER_BUILD: targets to make after a build
+#
+#    BEFORE_INSTALL: targets to build before installing the product
+#    AFTER_INSTALL: targets to build after installing the product
+#    BEFORE_POSTINSTALL: targets to build before postinstalling every subproject
+#    AFTER_POSTINSTALL: targts to build after postinstalling every subproject
+#
+#    BEFORE_INSTALLHDRS: targets to build before installing headers for a
+#         subproject
+#    AFTER_INSTALLHDRS: targets to build after installing headers for a subproject
+#    BEFORE_INSTALLSRC: targets to build before installing source for a subproject
+#    AFTER_INSTALLSRC: targets to build after installing source for a subproject
+#
+#    BEFORE_DEPEND: targets to build before building dependencies for a
+#        subproject
+#    AFTER_DEPEND: targets to build after building dependencies for a
+#        subproject
+#
+#    AUTOMATIC_DEPENDENCY_INFO: if YES, then the dependency file is
+#        updated every time the project is built.  If NO, the dependency
+#        file is only built when the depend target is invoked.
+
+# Framework-related variables:
+#    FRAMEWORK_DLL_INSTALLDIR:  On Windows platforms, this variable indicates
+#      where to put the framework's DLL.  This variable defaults to
+#      $(INSTALLDIR)/../Executables
+
+# Library-related variables:
+#    PUBLIC_HEADER_DIR:  Determines where public exported header files
+#      should be installed.  Do not include $(DSTROOT) in this value --
+#      it is prefixed automatically.  For library projects you should
+#       set this to something like /Developer/Headers/$(NAME).  Do not set
+#       this variable for framework projects unless you do not want the
+#       header files included in the framework.
+#    PRIVATE_HEADER_DIR:  Determines where private exported header files
+#      should be installed.  Do not include $(DSTROOT) in this value --
+#      it is prefixed automatically.
+#    LIBRARY_STYLE:  This may be either STATIC or DYNAMIC, and determines
+#      whether the libraries produced are statically linked when they
+#      are used or if they are dynamically loadable. This defaults to
+#       DYNAMIC.
+#    LIBRARY_DLL_INSTALLDIR:  On Windows platforms, this variable indicates
+#      where to put the library's DLL.  This variable defaults to
+#      $(INSTALLDIR)/../Executables
+#
+#    INSTALL_AS_USER: owner of the intalled products (default root)
+#    INSTALL_AS_GROUP: group of the installed products (default wheel)
+#    INSTALL_PERMISSIONS: permissions of the installed product (default o+rX)
+#
+#    OTHER_RECURSIVE_VARIABLES: The names of variables which you want to be
+#      passed on the command line to recursive invocations of make.  Note that
+#      the values in OTHER_*FLAGS are inherited by subprojects automatically --
+#      you do not have to (and shouldn't) add OTHER_*FLAGS to
+#      OTHER_RECURSIVE_VARIABLES.
+
+# Additional headers to export beyond those in the PB.project:
+#    OTHER_PUBLIC_HEADERS
+#    OTHER_PROJECT_HEADERS
+#    OTHER_PRIVATE_HEADERS
+
+# Additional files for the project's product: <<path relative to proj?>>
+#    OTHER_RESOURCES: (non-localized) resources for this project
+#    OTHER_OFILES: relocatables to be linked into this project
+#    OTHER_LIBS: more libraries to link against
+#    OTHER_PRODUCT_DEPENDS: other dependencies of this project
+#    OTHER_SOURCEFILES: other source files maintained by .pre/postamble
+#    OTHER_GARBAGE: additional files to be removed by `make clean'
+
+# Set this to YES if you don't want a final libtool call for a library/framework.
+#    BUILD_OFILES_LIST_ONLY
+
+# To include a version string, project source must exist in a directory named
+# $(NAME).%d[.%d][.%d] and the following line must be uncommented.
+# OTHER_GENERATED_OFILES = $(VERS_OFILE)
+
+# This definition will suppress stripping of debug symbols when an executable
+# is installed.  By default it is YES.
+# STRIP_ON_INSTALL = NO
+
+# Uncomment to suppress generation of a KeyValueCoding index when installing
+# frameworks (This index is used by WOB and IB to determine keys available
+# for an object).  Set to YES by default.
+# PREINDEX_FRAMEWORK = NO
+
+# Change this definition to install projects somewhere other than the
+# standard locations.  NEXT_ROOT defaults to "C:/Apple" on Windows systems
+# and "" on other systems.
+DSTROOT = $(HOME)
diff --git a/PB.project b/PB.project
new file mode 100644 (file)
index 0000000..3af9f55
--- /dev/null
@@ -0,0 +1,22 @@
+{
+    DYNAMIC_CODE_GEN = YES; 
+    FILESTABLE = {
+        H_FILES = (); 
+        OTHER_SOURCES = (Makefile.preamble, Makefile, Makefile.postamble); 
+        SUBPROJECTS = (SystemConfiguration.fproj, configd.tproj, scselect.tproj, scutil.tproj); 
+    }; 
+    LANGUAGE = English; 
+    MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; 
+    NEXTSTEP_BUILDTOOL = /bin/gnumake; 
+    NEXTSTEP_JAVA_COMPILER = /usr/bin/javac; 
+    NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc; 
+    PDO_UNIX_BUILDTOOL = $NEXT_ROOT/Developer/bin/make; 
+    PDO_UNIX_JAVA_COMPILER = "$(JDKBINDIR)/javac"; 
+    PDO_UNIX_OBJCPLUS_COMPILER = "$(NEXTDEV_BIN)/gcc"; 
+    PROJECTNAME = configd; 
+    PROJECTTYPE = Aggregate; 
+    PROJECTVERSION = 2.8; 
+    WINDOWS_BUILDTOOL = $NEXT_ROOT/Developer/Executables/make; 
+    WINDOWS_JAVA_COMPILER = "$(JDKBINDIR)/javac.exe"; 
+    WINDOWS_OBJCPLUS_COMPILER = "$(DEVDIR)/gcc"; 
+}
diff --git a/SystemConfiguration.fproj/CustomInfo.plist b/SystemConfiguration.fproj/CustomInfo.plist
new file mode 100644 (file)
index 0000000..89cf132
--- /dev/null
@@ -0,0 +1,5 @@
+{
+       CFBundleName = "SystemConfiguration";
+       CFBundleIdentifier = "com.apple.SystemConfiguration";
+       CFBundleShortVersionString = "1.0.0";
+}
diff --git a/SystemConfiguration.fproj/Makefile b/SystemConfiguration.fproj/Makefile
new file mode 100644 (file)
index 0000000..20f1e2c
--- /dev/null
@@ -0,0 +1,73 @@
+#
+# Generated by the Apple Project Builder.
+#
+# NOTE: Do NOT change this file -- Project Builder maintains it.
+#
+# Put all of your customizations in files called Makefile.preamble
+# and Makefile.postamble (both optional), and Makefile will include them.
+#
+
+NAME = SystemConfiguration
+
+PROJECTVERSION = 2.8
+PROJECT_TYPE = Framework
+
+HFILES = SystemConfiguration.h config_types.h SCD.h SCDKeys.h\
+         SCDPrivate.h SCP.h SCPPrivate.h SCPPath.h SCDConsoleUser.h\
+         SCDHostName.h SCNetwork.h ppp_msg.h ppp.h
+
+CFILES = SCD.c SCDKeys.c SCDPrivate.c SCDHandle.c SCDOpen.c\
+         SCDClose.c SCDLock.c SCDUnlock.c SCDList.c SCDAdd.c\
+         SCDAddSession.c SCDGet.c SCDSet.c SCDRemove.c SCDTouch.c\
+         SCDNotifierList.c SCDNotifierAdd.c SCDNotifierRemove.c\
+         SCDNotifierGetChanges.c SCDNotifierWait.c\
+         SCDNotifierInformViaCallback.c SCDNotifierInformViaMachPort.c\
+         SCDNotifierInformViaFD.c SCDNotifierInformViaSignal.c\
+         SCDNotifierCancel.c SCDSnapshot.c SCP.c SCPOpen.c SCPClose.c\
+         SCPLock.c SCPUnlock.c SCPList.c SCPGet.c SCPAdd.c SCPSet.c\
+         SCPRemove.c SCPCommit.c SCPApply.c SCPPath.c SCDConsoleUser.c\
+         SCDHostName.c SCNetwork.c ppp.c
+
+OTHERSRCS = Makefile.preamble Makefile Makefile.postamble m.template\
+            h.template config.defs genSCPreferences.c CustomInfo.plist
+
+
+MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles
+CURRENTLY_ACTIVE_VERSION = YES
+DEPLOY_WITH_VERSION_NAME = A
+CODE_GEN_STYLE = DYNAMIC
+MAKEFILE = framework.make
+NEXTSTEP_INSTALLDIR = $(SYSTEM_LIBRARY_DIR)/PrivateFrameworks
+WINDOWS_INSTALLDIR = /Library/Frameworks
+PDO_UNIX_INSTALLDIR = /Library/Frameworks
+LIBS = 
+DEBUG_LIBS = $(LIBS)
+PROF_LIBS = $(LIBS)
+
+
+FRAMEWORKS = -framework CoreFoundation
+PUBLIC_HEADERS = SystemConfiguration.h SCD.h SCDKeys.h SCP.h SCPPath.h\
+                 SCDConsoleUser.h SCDHostName.h SCNetwork.h
+
+PROJECT_HEADERS = SystemConfiguration.h config_types.h SCD.h\
+                  SCDPrivate.h SCP.h SCPPrivate.h SCPPath.h\
+                  SCDConsoleUser.h SCDHostName.h SCNetwork.h
+
+
+
+NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc
+WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc
+PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc
+NEXTSTEP_JAVA_COMPILER = /usr/bin/javac
+WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe
+PDO_UNIX_JAVA_COMPILER = $(JDKBINDIR)/javac
+
+include $(MAKEFILEDIR)/platform.make
+
+-include Makefile.preamble
+
+include $(MAKEFILEDIR)/$(MAKEFILE)
+
+-include Makefile.postamble
+
+-include Makefile.dependencies
diff --git a/SystemConfiguration.fproj/Makefile.postamble b/SystemConfiguration.fproj/Makefile.postamble
new file mode 100644 (file)
index 0000000..888849f
--- /dev/null
@@ -0,0 +1,119 @@
+###############################################################################
+#  Makefile.postamble
+#  Copyright 1997, Apple Computer, Inc.
+#
+#  Use this makefile, which is imported after all other makefiles, to
+#  override attributes for a project's Makefile environment. This allows you
+#  to take advantage of the environment set up by the other Makefiles.
+#  You can also define custom rules at the end of this file.
+#
+###############################################################################
+#
+# These variables are exported by the standard makefiles and can be
+# used in any customizations you make.  They are *outputs* of
+# the Makefiles and should be used, not set.
+#
+#  PRODUCTS: products to install.  All of these products will be placed in
+#       the directory $(DSTROOT)$(INSTALLDIR)
+#  GLOBAL_RESOURCE_DIR: The directory to which resources are copied.
+#  LOCAL_RESOURCE_DIR: The directory to which localized resources are copied.
+#  OFILE_DIR: Directory into which .o object files are generated.
+#  DERIVED_SRC_DIR: Directory used for all other derived files
+#
+#  ALL_CFLAGS:  flags to pass when compiling .c files
+#  ALL_MFLAGS:  flags to pass when compiling .m files
+#  ALL_CCFLAGS:  flags to pass when compiling .cc, .cxx, and .C files
+#  ALL_MMFLAGS:  flags to pass when compiling .mm, .mxx, and .M files
+#  ALL_PRECOMPFLAGS:  flags to pass when precompiling .h files
+#  ALL_LDFLAGS:  flags to pass when linking object files
+#  ALL_LIBTOOL_FLAGS:  flags to pass when libtooling object files
+#  ALL_PSWFLAGS:  flags to pass when processing .psw and .pswm (pswrap) files
+#  ALL_RPCFLAGS:  flags to pass when processing .rpc (rpcgen) files
+#  ALL_YFLAGS:  flags to pass when processing .y (yacc) files
+#  ALL_LFLAGS:  flags to pass when processing .l (lex) files
+#
+#  NAME: name of application, bundle, subproject, palette, etc.
+#  LANGUAGES: langages in which the project is written (default "English")
+#  English_RESOURCES: localized resources (e.g. nib's, images) of project
+#  GLOBAL_RESOURCES: non-localized resources of project
+#
+#  SRCROOT:  base directory in which to place the new source files
+#  SRCPATH:  relative path from SRCROOT to present subdirectory
+#
+#  INSTALLDIR: Directory the product will be installed into by 'install' target
+#  PUBLIC_HDR_INSTALLDIR: where to install public headers.  Don't forget
+#        to prefix this with DSTROOT when you use it.
+#  PRIVATE_HDR_INSTALLDIR: where to install private headers.  Don't forget
+#       to prefix this with DSTROOT when you use it.
+#
+#  EXECUTABLE_EXT: Executable extension for the platform (i.e. .exe on Windows)
+#
+###############################################################################
+
+# Some compiler flags can be overridden here for certain build situations.
+#
+#    WARNING_CFLAGS:  flag used to set warning level (defaults to -Wmost)
+#    DEBUG_SYMBOLS_CFLAGS:  debug-symbol flag passed to all builds (defaults
+#      to -g)
+#    DEBUG_BUILD_CFLAGS:  flags passed during debug builds (defaults to -DDEBUG)
+#    OPTIMIZE_BUILD_CFLAGS:  flags passed during optimized builds (defaults
+#      to -O)
+#    PROFILE_BUILD_CFLAGS:  flags passed during profile builds (defaults
+#      to -pg -DPROFILE)
+#    LOCAL_DIR_INCLUDE_DIRECTIVE:  flag used to add current directory to
+#      the include path (defaults to -I.)
+#    DEBUG_BUILD_LDFLAGS, OPTIMIZE_BUILD_LDFLAGS, PROFILE_BUILD_LDFLAGS: flags
+#      passed to ld/libtool (defaults to nothing)
+
+
+# Library and Framework projects only:
+#    INSTALL_NAME_DIRECTIVE:  This directive ensures that executables linked
+#      against the framework will run against the correct version even if
+#      the current version of the framework changes.  You may override this
+#      to "" as an alternative to using the DYLD_LIBRARY_PATH during your
+#      development cycle, but be sure to restore it before installing.
+
+
+# Ownership and permissions of files installed by 'install' target
+
+#INSTALL_AS_USER = root
+       # User/group ownership
+#INSTALL_AS_GROUP = wheel
+       # (probably want to set both of these)
+#INSTALL_PERMISSIONS =
+       # If set, 'install' chmod's executable to this
+
+
+# Options to strip.  Note: -S strips debugging symbols (executables can be stripped
+# down further with -x or, if they load no bundles, with no options at all).
+
+#STRIPFLAGS = -S
+
+
+#########################################################################
+# Put rules to extend the behavior of the standard Makefiles here.  Include them in
+# the dependency tree via cvariables like AFTER_INSTALL in the Makefile.preamble.
+#
+# You should avoid redefining things like "install" or "app", as they are
+# owned by the top-level Makefile API and no context has been set up for where
+# derived files should go.
+#
+
+#
+# Additional options to create generated headers, source
+#
+before_installhdrs: $(OFILE_DIR)
+
+$(OFILE_DIR)/genSCPreferences: genSCPreferences.o
+       $(CC) -o $@ $(ARCHITECTURE_FLAGS) $<
+
+SCPreferences.h: $(OFILE_DIR)/genSCPreferences
+       $(CD) $(SFILE_DIR) && $(OFILE_DIR)/genSCPreferences header > $@
+
+SCPreferences.c: $(OFILE_DIR)/genSCPreferences
+       $(CD) $(SFILE_DIR) && $(OFILE_DIR)/genSCPreferences cfile  > $@
+
+genSCFiles:
+       cc -o /tmp/genSCFiles genSCPreferences.c -framework CoreFoundation
+       /tmp/genSCFiles header > /tmp/SCPreferences.h
+       /tmp/genSCFiles cfile  > /tmp/SCPreferences.c
diff --git a/SystemConfiguration.fproj/Makefile.preamble b/SystemConfiguration.fproj/Makefile.preamble
new file mode 100644 (file)
index 0000000..cb94833
--- /dev/null
@@ -0,0 +1,159 @@
+###############################################################################
+#  Makefile.preamble
+#  Copyright 1997, Apple Computer, Inc.
+#
+#  Use this makefile for configuring the standard application makefiles
+#  associated with ProjectBuilder. It is included before the main makefile.
+#  In Makefile.preamble you set attributes for a project, so they are available
+#  to the project's makefiles.  In contrast, you typically write additional rules or
+#  override built-in behavior in the Makefile.postamble.
+#
+#  Each directory in a project tree (main project plus subprojects) should
+#  have its own Makefile.preamble and Makefile.postamble.
+###############################################################################
+#
+# Before the main makefile is included for this project, you may set:
+#
+#    MAKEFILEDIR: Directory in which to find $(MAKEFILE)
+#    MAKEFILE: Top level mechanism Makefile (e.g., app.make, bundle.make)
+
+# Compiler/linker flags added to the defaults:  The OTHER_* variables will be
+# inherited by all nested sub-projects, but the LOCAL_ versions of the same
+# variables will not.  Put your -I, -D, -U, and -L flags in ProjectBuilder's
+# Build Attributes inspector if at all possible.  To override the default flags
+# that get passed to ${CC} (e.g. change -O to -O2), see Makefile.postamble.  The
+# variables below are *inputs* to the build process and distinct from the override
+# settings done (less often) in the Makefile.postamble.
+#
+#    OTHER_CFLAGS, LOCAL_CFLAGS:  additional flags to pass to the compiler
+#      Note that $(OTHER_CFLAGS) and $(LOCAL_CFLAGS) are used for .h, ...c, .m,
+#      .cc, .cxx, .C, and .M files.  There is no need to respecify the
+#      flags in OTHER_MFLAGS, etc.
+#    OTHER_MFLAGS, LOCAL_MFLAGS:  additional flags for .m files
+#    OTHER_CCFLAGS, LOCAL_CCFLAGS:  additional flags for .cc, .cxx, and ...C files
+#    OTHER_MMFLAGS, LOCAL_MMFLAGS:  additional flags for .mm and .M files
+#    OTHER_PRECOMPFLAGS, LOCAL_PRECOMPFLAGS:  additional flags used when
+#      precompiling header files
+#    OTHER_LDFLAGS, LOCAL_LDFLAGS:  additional flags passed to ld and libtool
+#    OTHER_PSWFLAGS, LOCAL_PSWFLAGS:  additional flags passed to pswrap
+#    OTHER_RPCFLAGS, LOCAL_RPCFLAGS:  additional flags passed to rpcgen
+#    OTHER_YFLAGS, LOCAL_YFLAGS:  additional flags passed to yacc
+#    OTHER_LFLAGS, LOCAL_LFLAGS:  additional flags passed to lex
+
+# These variables provide hooks enabling you to add behavior at almost every
+# stage of the make:
+#
+#    BEFORE_PREBUILD: targets to build before installing headers for a subproject
+#    AFTER_PREBUILD: targets to build after installing headers for a subproject
+#    BEFORE_BUILD_RECURSION: targets to make before building subprojects
+#    BEFORE_BUILD: targets to make before a build, but after subprojects
+#    AFTER_BUILD: targets to make after a build
+#
+#    BEFORE_INSTALL: targets to build before installing the product
+#    AFTER_INSTALL: targets to build after installing the product
+#    BEFORE_POSTINSTALL: targets to build before postinstalling every subproject
+#    AFTER_POSTINSTALL: targts to build after postinstalling every subproject
+#
+#    BEFORE_INSTALLHDRS: targets to build before installing headers for a
+#         subproject
+#    AFTER_INSTALLHDRS: targets to build after installing headers for a subproject
+#    BEFORE_INSTALLSRC: targets to build before installing source for a subproject
+#    AFTER_INSTALLSRC: targets to build after installing source for a subproject
+#
+#    BEFORE_DEPEND: targets to build before building dependencies for a
+#        subproject
+#    AFTER_DEPEND: targets to build after building dependencies for a
+#        subproject
+#
+#    AUTOMATIC_DEPENDENCY_INFO: if YES, then the dependency file is
+#        updated every time the project is built.  If NO, the dependency
+#        file is only built when the depend target is invoked.
+
+# Framework-related variables:
+#    FRAMEWORK_DLL_INSTALLDIR:  On Windows platforms, this variable indicates
+#      where to put the framework's DLL.  This variable defaults to
+#      $(INSTALLDIR)/../Executables
+
+# Library-related variables:
+#    PUBLIC_HEADER_DIR:  Determines where public exported header files
+#      should be installed.  Do not include $(DSTROOT) in this value --
+#      it is prefixed automatically.  For library projects you should
+#       set this to something like /Developer/Headers/$(NAME).  Do not set
+#       this variable for framework projects unless you do not want the
+#       header files included in the framework.
+#    PRIVATE_HEADER_DIR:  Determines where private exported header files
+#      should be installed.  Do not include $(DSTROOT) in this value --
+#      it is prefixed automatically.
+#    LIBRARY_STYLE:  This may be either STATIC or DYNAMIC, and determines
+#      whether the libraries produced are statically linked when they
+#      are used or if they are dynamically loadable. This defaults to
+#       DYNAMIC.
+#    LIBRARY_DLL_INSTALLDIR:  On Windows platforms, this variable indicates
+#      where to put the library's DLL.  This variable defaults to
+#      $(INSTALLDIR)/../Executables
+#
+#    INSTALL_AS_USER: owner of the intalled products (default root)
+#    INSTALL_AS_GROUP: group of the installed products (default wheel)
+#    INSTALL_PERMISSIONS: permissions of the installed product (default o+rX)
+#
+#    OTHER_RECURSIVE_VARIABLES: The names of variables which you want to be
+#      passed on the command line to recursive invocations of make.  Note that
+#      the values in OTHER_*FLAGS are inherited by subprojects automatically --
+#      you do not have to (and shouldn't) add OTHER_*FLAGS to
+#      OTHER_RECURSIVE_VARIABLES.
+
+# Additional headers to export beyond those in the PB.project:
+#    OTHER_PUBLIC_HEADERS
+#    OTHER_PROJECT_HEADERS
+#    OTHER_PRIVATE_HEADERS
+
+# Additional files for the project's product: <<path relative to proj?>>
+#    OTHER_RESOURCES: (non-localized) resources for this project
+#    OTHER_OFILES: relocatables to be linked into this project
+#    OTHER_LIBS: more libraries to link against
+#    OTHER_PRODUCT_DEPENDS: other dependencies of this project
+#    OTHER_SOURCEFILES: other source files maintained by .pre/postamble
+#    OTHER_GARBAGE: additional files to be removed by `make clean'
+
+# Set this to YES if you don't want a final libtool call for a library/framework.
+#    BUILD_OFILES_LIST_ONLY
+
+# To include a version string, project source must exist in a directory named
+# $(NAME).%d[.%d][.%d] and the following line must be uncommented.
+OTHER_GENERATED_OFILES = $(VERS_OFILE)
+
+# This definition will suppress stripping of debug symbols when an executable
+# is installed.  By default it is YES.
+# STRIP_ON_INSTALL = NO
+
+# Uncomment to suppress generation of a KeyValueCoding index when installing
+# frameworks (This index is used by WOB and IB to determine keys available
+# for an object).  Set to YES by default.
+# PREINDEX_FRAMEWORK = NO
+
+# Change this definition to install projects somewhere other than the
+# standard locations.  NEXT_ROOT defaults to "C:/Apple" on Windows systems
+# and "" on other systems.
+# DSTROOT = $(HOME)
+
+# Additional flags for MiG generated files
+OTHER_PROJECT_HEADERS += config.defs config.h
+OTHER_OFILES          += configUser.o
+
+# Additional options to create generated headers, source
+BEFORE_INSTALLHDRS       =  before_installhdrs
+OTHER_SOURCEFILES        += genSCPreferences.c
+OTHER_GENERATED_SRCFILES += SCPreferences.h SCPreferences.c
+OTHER_PUBLIC_HEADERS     += SCPreferences.h
+OTHER_OFILES             += SCPreferences.o
+
+# Specify framework initialization code
+OTHER_LDFLAGS += -Wl,-init,___Initialize
+
+# Additional build flags
+ifeq "$(PLATFORM_OS)" "macos"
+       APPLE_INTERNAL_DIR ?= /AppleInternal
+       APPLE_INTERNAL_DEVELOPER_DIR ?= /AppleInternal/Developer
+       OTHER_LDFLAGS += -seg_addr_table $(APPLE_INTERNAL_DEVELOPER_DIR)/seg_addr_table
+       SECTORDER_FLAGS = -sectorder __TEXT __text $(APPLE_INTERNAL_DIR)/OrderFiles/SystemConfiguration.order
+endif
diff --git a/SystemConfiguration.fproj/PB.project b/SystemConfiguration.fproj/PB.project
new file mode 100644 (file)
index 0000000..f132d48
--- /dev/null
@@ -0,0 +1,120 @@
+{
+    CURRENTLY_ACTIVE_VERSION = YES; 
+    DEPLOY_WITH_VERSION_NAME = A; 
+    DYNAMIC_CODE_GEN = YES; 
+    FILESTABLE = {
+        FRAMEWORKS = (CoreFoundation.framework); 
+        FRAMEWORKSEARCH = (); 
+        H_FILES = (
+            SystemConfiguration.h, 
+            config_types.h, 
+            SCD.h, 
+            SCDKeys.h, 
+            SCDPrivate.h, 
+            SCP.h, 
+            SCPPrivate.h, 
+            SCPPath.h, 
+            SCDConsoleUser.h, 
+            SCDHostName.h, 
+            SCNetwork.h, 
+            ppp_msg.h, 
+            ppp.h
+        ); 
+        OTHER_LINKED = (
+            SCD.c, 
+            SCDKeys.c, 
+            SCDPrivate.c, 
+            SCDHandle.c, 
+            SCDOpen.c, 
+            SCDClose.c, 
+            SCDLock.c, 
+            SCDUnlock.c, 
+            SCDList.c, 
+            SCDAdd.c, 
+            SCDAddSession.c, 
+            SCDGet.c, 
+            SCDSet.c, 
+            SCDRemove.c, 
+            SCDTouch.c, 
+            SCDNotifierList.c, 
+            SCDNotifierAdd.c, 
+            SCDNotifierRemove.c, 
+            SCDNotifierGetChanges.c, 
+            SCDNotifierWait.c, 
+            SCDNotifierInformViaCallback.c, 
+            SCDNotifierInformViaMachPort.c, 
+            SCDNotifierInformViaFD.c, 
+            SCDNotifierInformViaSignal.c, 
+            SCDNotifierCancel.c, 
+            SCDSnapshot.c, 
+            SCP.c, 
+            SCPOpen.c, 
+            SCPClose.c, 
+            SCPLock.c, 
+            SCPUnlock.c, 
+            SCPList.c, 
+            SCPGet.c, 
+            SCPAdd.c, 
+            SCPSet.c, 
+            SCPRemove.c, 
+            SCPCommit.c, 
+            SCPApply.c, 
+            SCPPath.c, 
+            SCDConsoleUser.c, 
+            SCDHostName.c, 
+            SCNetwork.c, 
+            ppp.c
+        ); 
+        OTHER_SOURCES = (
+            Makefile.preamble, 
+            Makefile, 
+            Makefile.postamble, 
+            m.template, 
+            h.template, 
+            config.defs, 
+            genSCPreferences.c, 
+            CustomInfo.plist
+        ); 
+        PRECOMPILED_HEADERS = (); 
+        PROJECT_HEADERS = (
+            SystemConfiguration.h, 
+            config_types.h, 
+            SCD.h, 
+            SCDPrivate.h, 
+            SCP.h, 
+            SCPPrivate.h, 
+            SCPPath.h, 
+            SCDConsoleUser.h, 
+            SCDHostName.h, 
+            SCNetwork.h
+        ); 
+        PUBLIC_HEADERS = (
+            SystemConfiguration.h, 
+            SCD.h, 
+            SCDKeys.h, 
+            SCP.h, 
+            SCPPath.h, 
+            SCDConsoleUser.h, 
+            SCDHostName.h, 
+            SCNetwork.h
+        ); 
+        SUBPROJECTS = (); 
+    }; 
+    LANGUAGE = English; 
+    MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; 
+    NEXTSTEP_BUILDTOOL = /usr/bin/gnumake; 
+    NEXTSTEP_INSTALLDIR = "$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks"; 
+    NEXTSTEP_JAVA_COMPILER = /usr/bin/javac; 
+    NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc; 
+    PDO_UNIX_BUILDTOOL = $NEXT_ROOT/Developer/bin/make; 
+    PDO_UNIX_INSTALLDIR = /Library/Frameworks; 
+    PDO_UNIX_JAVA_COMPILER = "$(JDKBINDIR)/javac"; 
+    PDO_UNIX_OBJCPLUS_COMPILER = "$(NEXTDEV_BIN)/gcc"; 
+    PROJECTNAME = SystemConfiguration; 
+    PROJECTTYPE = Framework; 
+    PROJECTVERSION = 2.8; 
+    WINDOWS_BUILDTOOL = $NEXT_ROOT/Developer/Executables/make; 
+    WINDOWS_INSTALLDIR = /Library/Frameworks; 
+    WINDOWS_JAVA_COMPILER = "$(JDKBINDIR)/javac.exe"; 
+    WINDOWS_OBJCPLUS_COMPILER = "$(DEVDIR)/gcc"; 
+}
diff --git a/SystemConfiguration.fproj/SCD.c b/SystemConfiguration.fproj/SCD.c
new file mode 100644 (file)
index 0000000..169af6b
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include <SystemConfiguration/SCD.h>
+#include "config.h"            /* MiG generated file */
+#include "SCDPrivate.h"
+
+
+static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
+
+static _SCDFlags       globalFlags = {
+       FALSE           /* debug        */,
+       FALSE           /* verbose      */,
+       FALSE           /* useSyslog    */,
+       FALSE           /* isLocked     */,
+       TRUE            /* useCFRunLoop */,
+};
+
+static boolean_t       isSCDServer = FALSE;
+
+static const struct scd_errmsg {
+       SCDStatus       status;
+       char            *message;
+} scd_errmsgs[] = {
+       { SCD_OK,               "Success!" },
+       { SCD_NOSESSION,        "Configuration daemon session not active" },
+       { SCD_NOSERVER,         "Configuration daemon not (no longer) available" },
+       { SCD_LOCKED,           "Lock already held" },
+       { SCD_NEEDLOCK,         "Lock required for this operation" },
+       { SCD_EACCESS,          "Permission denied (must be root to obtain lock)" },
+       { SCD_NOKEY,            "No such key" },
+       { SCD_EXISTS,           "Data associated with key already defined" },
+       { SCD_STALE,            "Write attempted on stale version of object" },
+       { SCD_INVALIDARGUMENT,  "Invalid argument" },
+       { SCD_NOTIFIERACTIVE,   "Notifier is currently active" },
+       { SCD_FAILED,           "Failed!" }
+};
+#define nSCD_ERRMSGS (sizeof(scd_errmsgs)/sizeof(struct scd_errmsg))
+
+
+int
+SCDOptionGet(SCDSessionRef session, const int option)
+{
+       _SCDFlags       *theFlags = &globalFlags;
+       int             value     = 0;
+
+       if (session != NULL) {
+               theFlags = &((SCDSessionPrivateRef)session)->flags;
+       }
+
+       switch (option) {
+
+               /* session dependent flags */
+
+               case kSCDOptionDebug :
+                       value = theFlags->debug;
+                       break;
+
+               case kSCDOptionVerbose :
+                       value = theFlags->verbose;
+                       break;
+
+               case kSCDOptionIsLocked :
+                       value = theFlags->isLocked;
+                       break;
+
+               case kSCDOptionUseSyslog :
+                       value = theFlags->useSyslog;
+                       break;
+
+               case kSCDOptionUseCFRunLoop :
+                       value = theFlags->useCFRunLoop;
+                       break;
+
+               /* session independent flags */
+
+               case kSCDOptionIsServer :
+                       value = isSCDServer;
+                       break;
+       }
+
+       return value;
+}
+
+
+void
+SCDOptionSet(SCDSessionRef session, int option, const int value)
+{
+       _SCDFlags       *theFlags = &globalFlags;
+
+       if (session != NULL) {
+               theFlags = &((SCDSessionPrivateRef)session)->flags;
+       }
+
+       switch (option) {
+
+               /* session independent flags */
+
+               case kSCDOptionDebug :
+                       theFlags->debug = value;
+                       break;
+
+               case kSCDOptionVerbose :
+                       theFlags->verbose = value;
+                       break;
+
+               case kSCDOptionIsLocked :
+                       theFlags->isLocked = value;
+                       break;
+
+               case kSCDOptionUseSyslog :
+                       theFlags->useSyslog = value;
+                       break;
+
+               case kSCDOptionUseCFRunLoop :
+                       theFlags->useCFRunLoop = value;
+                       break;
+
+               /* session independent flags */
+
+               case kSCDOptionIsServer :
+                       isSCDServer = value;
+                       break;
+       }
+
+       return;
+}
+
+
+static void
+_SCDLog(SCDSessionRef session, int level, CFArrayRef lines)
+{
+       FILE            *f = (LOG_PRI(level) > LOG_NOTICE) ? stderr : stdout;
+       CFDataRef       line;
+       int             i;
+
+       if ((LOG_PRI(level) == LOG_DEBUG) && !SCDOptionGet(session, kSCDOptionVerbose)) {
+               /* it's a debug message and we haven't requested verbose logging */
+               return;
+       }
+
+       pthread_mutex_lock(&lock);
+
+       for (i=0; i<CFArrayGetCount(lines); i++) {
+               line = CFStringCreateExternalRepresentation(NULL,
+                                                           CFArrayGetValueAtIndex(lines, i),
+                                                           kCFStringEncodingMacRoman,
+                                                           '?');
+               if (line != NULL) {
+                       if (SCDOptionGet(session, kSCDOptionUseSyslog)) {
+                               syslog (level,  "%.*s",   (int)CFDataGetLength(line), CFDataGetBytePtr(line));
+                       } else {
+                               fprintf(f, "%.*s\n", (int)CFDataGetLength(line), CFDataGetBytePtr(line));
+                               fflush (f);
+                       }
+                       CFRelease(line);
+               }
+       }
+
+       pthread_mutex_unlock(&lock);
+}
+
+
+void
+SCDSessionLog(SCDSessionRef session, int level, CFStringRef formatString, ...)
+{
+       va_list         argList;
+       CFStringRef     resultString;
+       CFArrayRef      lines;
+
+       va_start(argList, formatString);
+       resultString = CFStringCreateWithFormatAndArguments(NULL, NULL, formatString, argList);
+       va_end(argList);
+
+       lines = CFStringCreateArrayBySeparatingStrings(NULL, resultString, CFSTR("\n"));
+       _SCDLog(session, level, lines);
+       CFRelease(lines);
+       CFRelease(resultString);
+}
+
+
+void
+SCDLog(int level, CFStringRef formatString, ...)
+{
+       va_list         argList;
+       CFStringRef     resultString;
+       CFArrayRef      lines;
+
+       va_start(argList, formatString);
+       resultString = CFStringCreateWithFormatAndArguments(NULL, NULL, formatString, argList);
+       va_end(argList);
+
+       lines = CFStringCreateArrayBySeparatingStrings(NULL, resultString, CFSTR("\n"));
+       _SCDLog(NULL, level, lines);
+       CFRelease(lines);
+       CFRelease(resultString);
+}
+
+
+const char *
+SCDError(SCDStatus status)
+{
+       int i;
+
+       for (i = 0; i < nSCD_ERRMSGS; i++) {
+               if (scd_errmsgs[i].status == status) {
+                       return scd_errmsgs[i].message;
+               }
+       }
+       return "(unknown error)";
+}
diff --git a/SystemConfiguration.fproj/SCD.h b/SystemConfiguration.fproj/SCD.h
new file mode 100644 (file)
index 0000000..d580665
--- /dev/null
@@ -0,0 +1,631 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#ifndef _SCD_H
+#define _SCD_H
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <CoreFoundation/CFRunLoop.h>
+#include <mach/message.h>
+#include <sys/cdefs.h>
+#include <sys/syslog.h>
+
+
+/*!
+       @header SCD.h
+       The SystemConfiguration framework provides access to the data used to configure a running system.  The APIs provided by this framework communicate with the "configd" daemon.
+
+The "configd" daemon manages a "cache" reflecting the desired configuration settings as well as the current state of the system.  The daemon provides a notification mechanism for user-level processes which need to be aware of changes made to the "cache" data.  Lastly, the daemon loads a number of bundles(or plug-ins) which monitor low-level kernel events and, via a set of policy modules, keep this cached data up to date.
+
+The "configd" daemon also provides an address space/task/process which can be used by other CFRunLoop based functions which would otherwise require their own process/daemon for execution.
+
+ */
+
+
+/*!
+       @enum SCDStatus
+       @discussion Returned status codes.
+       @constant SCD_OK                Success
+       @constant SCD_NOSESSION         Configuration daemon session not active
+       @constant SCD_NOSERVER          Configuration daemon not (no longer) available
+       @constant SCD_LOCKED            Lock already held
+       @constant SCD_NEEDLOCK          Lock required for this operation
+       @constant SCD_EACCESS           Permission denied (must be root to obtain lock)
+       @constant SCD_NOKEY             No such key
+       @constant SCD_EXISTS            Data associated with key already defined
+       @constant SCD_STALE             Write attempted on stale version of object
+       @constant SCD_INVALIDARGUMENT   Invalid argument
+       @constant SCD_NOTIFIERACTIVE    Notifier is currently active
+       @constant SCD_FAILED            Generic error
+ */
+typedef enum {
+       SCD_OK                  = 0,    /* Success */
+       SCD_NOSESSION           = 1,    /* Configuration daemon session not active */
+       SCD_NOSERVER            = 2,    /* Configuration daemon not (no longer) available */
+       SCD_LOCKED              = 3,    /* Lock already held */
+       SCD_NEEDLOCK            = 4,    /* Lock required for this operation */
+       SCD_EACCESS             = 5,    /* Permission denied (must be root to obtain lock) */
+       SCD_NOKEY               = 6,    /* No such key */
+       SCD_EXISTS              = 7,    /* Data associated with key already defined */
+       SCD_STALE               = 8,    /* Write attempted on stale version of object */
+       SCD_INVALIDARGUMENT     = 9,    /* Invalid argument */
+       SCD_NOTIFIERACTIVE      = 10,   /* Notifier is currently active */
+       SCD_FAILED              = 9999  /* Generic error */
+} SCDStatus;
+
+
+/*!
+       @typedef SCDSessionRef
+       @discussion This is the type of a handle to an open "session" with the system
+               configuration daemon.
+ */
+typedef const struct __SCDSession *    SCDSessionRef;
+
+/*!
+       @typedef SCDHandleRef
+       @discussion This is the type of a handle to data being retrieved from or to be
+               stored by the system configuration daemon.
+ */
+typedef const struct __SCDHandle *     SCDHandleRef;
+
+
+/*!
+       @typedef SCDCallbackRoutine_t
+       @discussion Type of the callback function used by the SCDNotifierInformViaCallback()
+               function.
+       @param session The session handle.
+       @param callback_argument The user-defined argument to be passed to the callback
+               function.
+ */
+typedef boolean_t (*SCDCallbackRoutine_t)       (SCDSessionRef         session,
+                                                 void                  *callback_argument);
+
+
+/*!
+       @enum SCDKeyOption
+       @discussion Used with the SCDList() and SCDNotifierAdd() functions to describe
+               the CFStringRef argument.
+       @constant kSCDRegexKey Specifies that the key consists of a regular expression.
+ */
+typedef enum {
+       kSCDRegexKey            = 0100000,      /* pattern string is a regular expression */
+} SCDKeyOption;
+
+
+/*!
+       @enum SCDOption
+       @discussion Options which determine the run-time processing of the system
+               configuration framework functions.
+       @constant kSCDOptionDebug Enable debugging
+       @constant kSCDOptionVerbose Enable verbose logging
+       @constant kSCDOptionUseSyslog Use syslog(3) for log messages
+       @constant kSCDOptionUseCFRunLoop Calling application is CFRunLoop() based
+ */
+typedef enum {
+       kSCDOptionDebug         = 0,    /* Enable debugging */
+       kSCDOptionVerbose       = 1,    /* Enable verbose logging */
+       kSCDOptionUseSyslog     = 2,    /* Use syslog(3) for log messages */
+       kSCDOptionUseCFRunLoop  = 3,    /* Calling application is CFRunLoop() based */
+} SCDOption;
+
+
+/*
+ * "configd" loadable bundle / plug-in options
+ */
+
+/*!
+       @typedef SCDBundleStartRoutine_t
+       @discussion Type of the start() initializatioin function which will be called
+               after all plug-ins have been loaded.  This function should initialize any
+               variables, open any sessions with "configd", and register any needed
+               notifications.
+       @param bundlePath The path name associated with the plug-in / bundle.
+       @param bundleName The name of the plug-in / bundle.
+ */
+typedef void (*SCDBundleStartRoutine_t) (const char *bundlePath,
+                                        const char *bundleName);
+
+/*!
+       @typedef SCDBundlePrimeRoutine_t
+       @discussion Type of the prime() initializatioin function which will be
+               called after all plug-ins have executed their start() function but
+               before the main plug-in run loop is started.  This function should
+               be used to initialize any configuration information and/or state
+               in the cache.
+ */
+typedef void (*SCDBundlePrimeRoutine_t) ();
+
+#define        BUNDLE_DIRECTORY        "/SystemConfiguration"  /* [/System/Library]/... */
+#define BUNDLE_OLD_SUBDIR      "/"
+#define BUNDLE_NEW_SUBDIR      "/Contents/MacOS/"
+#define BUNDLE_DIR_EXTENSION   ".bundle"
+#define BUNDLE_DEBUG_EXTENSION "_debug"
+#define BUNDLE_ENTRY_POINT     "_start"
+#define BUNDLE_ENTRY_POINT2    "_prime"
+
+
+__BEGIN_DECLS
+
+/*!
+       @function SCDHandleInit
+       @discussion Creates a new handle used to access the cached configuration
+               dictionary data.
+       @result A new "cache" data handle.
+ */
+SCDHandleRef   SCDHandleInit                   ();
+
+/*!
+       @function SCDHandleRelease
+       @discussion Releases the specified configuration data handle. The dictionary
+               (or other CFType) associated with this handle will also be released
+               unless a call was previously made to CFRetain().
+       @param handle The cache data handle to be released.
+ */
+void           SCDHandleRelease                (SCDHandleRef           handle);
+
+/*!
+       @function SCDHandleGetInstance
+       @discussion Returns the instance number associated with the specified
+               configuration handle.
+       @param handle The cache data handle.
+       @result The instance number associated with the specified handle.
+ */
+int            SCDHandleGetInstance            (SCDHandleRef           handle);
+
+/*!
+       @function SCDHandleGetData
+       @discussion Returns a reference to the data associated with the specified
+               configuration handle.
+       @param handle The cache data handle.
+       @result The CFType data associated with the specified handle.
+ */
+CFPropertyListRef SCDHandleGetData             (SCDHandleRef           handle);
+
+/*!
+       @function SCDHandleSetData
+       @discussion Returns a reference to the data associated with the specified
+               configuration handle.
+       @param handle The cache data handle.
+       @param data The CFType data to be associated with the handle.
+ */
+void           SCDHandleSetData                (SCDHandleRef           handle,
+                                                CFPropertyListRef      data);
+/*!
+       @function SCDOpen
+       @discussion Initiates a connection with the configuration daemon.
+       @param session A pointer to memory which will be filled with an SCDSessionRef
+               handle to be used for all subsequent requests to the server.
+               If a session cannot be established with the server, the contents of
+               memory pointed to by this parameter are undefined.
+       @param name Pass a string which describes the name of the calling process or
+               plug-in name of the caller.
+       @result A constant of type SCDStatus indicating the success (or failure) of
+               the call.
+ */
+SCDStatus      SCDOpen                         (SCDSessionRef          *session,
+                                                CFStringRef            name);
+
+/*!
+       @function SCDClose
+       @discussion Closes the specified session to the configuration daemon.  All
+               outstanding notification requests will be cancelled.
+       @param session Pass a pointer to an SCDSessionRef handle which should be closed.
+       @result A constant of type SCDStatus indicating the success (or failure) of
+               the call.
+ */
+SCDStatus      SCDClose                        (SCDSessionRef          *session);
+
+/*!
+       @function SCDLock
+       @discussion Locks access to the configuration "cache".  All other clients
+               attempting to access the "cache" will block. All change notifications
+               will be deferred until the lock is released.
+       @param session Pass a SCDSessionRef handle which should be used for
+               communication with the server.
+       @result A constant of type SCDStatus indicating the success (or failure) of
+               the call.
+ */
+SCDStatus      SCDLock                         (SCDSessionRef          session);
+
+/*!
+       @function SCDUnlock
+       @discussion Unlocks access to the configuration "cache".  Other clients will
+               be able to access the "cache". Any change notifications will be delivered.
+       @param session Pass a SCDSessionRef handle which should be used for
+               communication with the server.
+       @result A constant of type SCDStatus indicating the success (or failure) of
+               the call.
+ */
+SCDStatus      SCDUnlock                       (SCDSessionRef          session);
+
+/*!
+       @function SCDList
+       @discussion Returns an array of CFStringRefs representing the configuration "cache"
+               entries which match the specified pattern key.
+       @param session Pass a SCDSessionRef handle which should be used for
+               communication with the server.
+       @param key The string which must prefix those keys in the configuration "cache"
+               (if regexOptions is zero) or a regex(3) regular expression string which
+               will be used to match configuration "cache" (if regexOptions is kSCDRegexKey).
+       @param regexOptions Pass a bitfield of type SCDKeyOption containing one or more
+               flags describing the pattern key.
+       @param subKeys Pass a pointer to a CFArrayRef which will be set to a new
+               array of CFStringRefs's matching the provided key.
+       @result A constant of type SCDStatus indicating the success (or failure) of
+               the call.
+ */
+SCDStatus      SCDList                         (SCDSessionRef          session,
+                                                CFStringRef            key,
+                                                int                    regexOptions,
+                                                CFArrayRef             *subKeys);
+
+/*!
+       @function SCDAdd
+       @discussion Creates a new entry in the configuration "cache" using the
+               specified key and data.  An error is returned if the key is already
+               defined in the dictionary.
+       @param session Pass a SCDSessionRef handle which should be used for
+               communication with the server.
+       @param key Pass a reference to the CFStringRef object to be created.
+       @param handle Pass a reference to the SCDHandle object containing the data
+               to be associated with the specified key.
+       @result A constant of type SCDStatus indicating the success (or failure) of
+               the call.
+ */
+SCDStatus      SCDAdd                          (SCDSessionRef          session,
+                                                CFStringRef            key,
+                                                SCDHandleRef           handle);
+
+/*!
+       @function SCDAddSession
+       @discussion Creates a new entry in the configuration "cache" using the
+       specified key and data.  This entry will, unless updated by another
+       session, automatically be removed when the session is closed.  An
+       error is returned if the key is already defined in the dictionary.
+
+       @param session Pass a SCDSessionRef handle which should be used for
+               communication with the server.
+       @param key Pass a reference to the CFStringRef object to be created.
+       @param handle Pass a reference to the SCDHandle object containing the data
+               to be associated with the specified key.
+       @result A constant of type SCDStatus indicating the success (or failure) of
+               the call.
+ */
+SCDStatus      SCDAddSession                   (SCDSessionRef          session,
+                                                CFStringRef            key,
+                                                SCDHandleRef           handle);
+
+/*!
+       @function SCDGet
+       @discussion Returns a handle to the configuration "cache" data that corresponds
+               to the specified key.
+       @param session Pass a SCDSessionRef handle which should be used for
+               communication with the server.
+       @param key Pass a reference to the CFStringRef object to be returned.
+       @param handle Pass a pointer to a SCDHandleRef which will be set to a
+               new object containing the data associated with the specified key.
+       @result A constant of type SCDStatus indicating the success (or failure) of
+               the call.
+ */
+SCDStatus      SCDGet                          (SCDSessionRef          session,
+                                                CFStringRef            key,
+                                                SCDHandleRef           *handle);
+
+/*!
+       @function SCDSet
+       @discussion Updates the entry in the configuration "cache" that corresponds to
+               the specified key with the provided data.  An error will be returned if
+               the data in the "cache" has been updated since the data handle was last
+               updated.
+       @param session Pass the SCDSessionRef handle which should be used to communicate
+               with the server.
+       @param key Pass a reference to the CFStringRef object to be updated.
+       @param handle Pass a reference to the SCDHandle object containing the data
+               to be associated with the specified key.
+       @result A constant of type SCDStatus indicating the success (or failure) of
+               the call.
+ */
+SCDStatus      SCDSet                          (SCDSessionRef          session,
+                                                CFStringRef            key,
+                                                SCDHandleRef           handle);
+
+/*!
+       @function SCDRemove
+       @discussion Removes the data from the configuration "cache" data which corresponds
+               to the specified key.
+       @param session Pass a SCDSessionRef handle which should be used for
+               communication with the server.
+       @param key Pass a reference to the CFStringRef object to be removed.
+       @result A constant of type SCDStatus indicating the success (or failure) of
+               the call.
+ */
+SCDStatus      SCDRemove                       (SCDSessionRef          session,
+                                                CFStringRef            key);
+
+/*!
+       @function SCDTouch
+       @discussion Updates the instance number for the data in the configuration "cache"
+               associated with the specified key.  If the specified key does not exist
+               then a CFDate object will be associated with the key.  If the associated
+               data is already a CFDate object then the value will be updated.
+       @param session Pass a SCDSessionRef handle which should be used for
+               communication with the server.
+       @param key Pass a reference to the CFStringRef object to be updated.
+       @result A constant of type SCDStatus indicating the success (or failure) of
+               the call.
+ */
+SCDStatus      SCDTouch                        (SCDSessionRef          session,
+                                               CFStringRef             key);
+
+/*
+       @function SCDSnapshot
+       @discussion Records the current state of configd's cache dictionary into the
+               /var/tmp/configd-cache file and configd's session dictionary into the
+               /var/tmp/configd-session file.
+       @result A constant of type SCDStatus indicating the success (or failure) of
+               the call.
+ */
+SCDStatus      SCDSnapshot                     (SCDSessionRef          session);
+
+/*!
+       @function SCDNotifierList
+       @discussion Returns an array of CFStringRefs representing the system configuration
+               data entries being monitored for changes.
+       @param session Pass a SCDSessionRef handle which should be used for
+               communication with the server.
+       @param regexOptions Pass a bitfield of type SCDKeyOption which specifies whether
+               the specific configuration cache key patterns are to be returned or those
+               based on regex(3) pattern strings.
+       @param notifierKeys Pass a pointer to a CFArrayRef which will be set to a new
+               array of CFStringRef's being monitored.
+       @result A constant of type SCDStatus indicating the success (or failure) of
+               the call.
+ */
+SCDStatus      SCDNotifierList                 (SCDSessionRef          session,
+                                                int                    regexOptions,
+                                                CFArrayRef             *notifierKeys);
+
+/*!
+       @function SCDNotifierAdd
+       @discussion Adds the specified key to the list of system configuration
+               data entries which are being monitored for changes.
+       @param session Pass a SCDSessionRef handle which should be used for
+               communication with the server.
+       @param key Pass a reference to the CFStringRef object to be monitored.
+       @param regexOptions Pass a bitfield of type SCDKeyOption which specifies whether
+               the key is for a specific configuration cache key or if it consists
+               of a regex(3) pattern string.
+       @result A constant of type SCDStatus indicating the success (or failure) of
+               the call.
+ */
+SCDStatus      SCDNotifierAdd                  (SCDSessionRef          session,
+                                                CFStringRef            key,
+                                                int                    regexOptions);
+
+/*!
+       @function SCDNotifierRemove
+       @discussion Removes the specified key from the list of system configuration
+               data entries which are being monitored for changes.
+       @param session Pass a SCDSessionRef handle which should be used for
+               communication with the server.
+       @param key Pass a reference to the CFStringRef object which should not be monitored.
+       @param regexOptions Pass a bitfield of type SCDKeyOption which specifies whether
+               the key is for a specific configuration cache key or if it consists
+               of a regex(3) pattern string.
+       @result A constant of type SCDStatus indicating the success (or failure) of
+               the call.
+ */
+SCDStatus      SCDNotifierRemove               (SCDSessionRef          session,
+                                                CFStringRef            key,
+                                                int                    regexOptions);
+
+/*!
+       @function SCDNotifierGetChanges
+       @discussion Returns an array of CFStringRefs representing the monitored system
+               configuration data entries which have changed since this function
+               was last called.
+       @param session Pass a SCDSessionRef handle which should be used for
+               communication with the server.
+       @param notifierKeys Pass a pointer to a CFArrayRef which will be set to a new
+               array of CFStringRef's being monitored.
+       @result A constant of type SCDStatus indicating the success (or failure) of
+               the call.
+ */
+SCDStatus      SCDNotifierGetChanges           (SCDSessionRef          session,
+                                                CFArrayRef             *notifierKeys);
+
+/*!
+       @function SCDNotifierWait
+       @discussion Waits for a change to be made to a system configuration data
+               entry associated with the current sessions notifier keys.
+
+       Note: this function is not valid for "configd" plug-ins.
+
+       @param session Pass a SCDSessionRef handle which should be used for
+               communication with the server.
+       @result A constant of type SCDStatus indicating the success (or failure) of
+               the call.
+ */
+SCDStatus      SCDNotifierWait                 (SCDSessionRef          session);
+
+/*!
+       @function SCDNotifierInformViaCallback
+       @discussion Requests that the specified function be called whenever a change
+               has been detected to one of the system configuration data entries
+               associated with the current sessions notifier keys.
+
+       The callback function will be called with two arguments, session and arg, which
+       correspond to the current session and the provided argument.
+       The function should return a boolean value indicating whether an
+       error occurred during execution of the callback.
+
+       Note: if the calling application is based on the CFRunLoop() then an additional
+       run loop source will be added for the notification.
+       Applications which are not based on the CFRunLoop() will have a separate thread
+       started to wait for changes.
+       In either case, the additional run loop source and/or thread will terminate if
+       the notification is cancelled or if the callback indicates that an error was
+       detected.
+
+       Note: this function is not valid for "configd" plug-ins.
+
+       @param session Pass a SCDSessionRef handle which should be used for
+               communication with the server.
+       @param func Pass a pointer to the callback applier function to call when a
+               monitored cache entry is changed.  If this parameter is not a pointer to
+               a function of the correct prototype (SCDCallbackRoutine_t), the behavior
+               is undefined.
+       @result A constant of type SCDStatus indicating the success (or failure) of
+               the call.
+ */
+SCDStatus      SCDNotifierInformViaCallback    (SCDSessionRef          session,
+                                                SCDCallbackRoutine_t   func,
+                                                void                   *arg);
+
+/*!
+       @function SCDNotifierInformViaMachPort
+       @discussion Allocates a mach port which can be used to detect changes to
+               one of the system configuration data entries associated with the
+               current sessions notifier keys. When a change is detected, an
+               empty (no data) mach message with the specified identifier will
+               be delivered to the calling application via the allocated port.
+
+       @param session Pass a SCDSessionRef handle which should be used for
+               communication with the server.
+       @param msgid Pass a mach message ID to be included with any notifications.
+       @param port Pass a pointer to a mach port.  Upon return, port will be filled
+               with the mach port which will be used for any notifications.
+       @result A constant of type SCDStatus indicating the success (or failure) of
+               the call.
+ */
+SCDStatus      SCDNotifierInformViaMachPort    (SCDSessionRef          session,
+                                                mach_msg_id_t          msgid,
+                                                mach_port_t            *port);
+
+/*!
+       @function SCDNotifierInformViaFD
+       @discussion Allocates a file descriptor which can be used to detect changes
+               to one of the system configuration data entries associated with the
+               current sessions notifier keys. When a change is detected, the
+               specified identifier (4 bytes) will be delivered to the calling
+               application via the allocated file descriptor.
+
+       @param session Pass a SCDSessionRef handle which should be used for
+               communication with the server.
+       @param identifier Pass an (4 byte) integer identifer which be used for any
+               notifications.
+       @param fd Pass a pointer to a file descriptor.  Upon return, fd will be filled
+               with the file descriptor which will be used for any notifications.
+       @result A constant of type SCDStatus indicating the success (or failure) of
+               the call.
+ */
+SCDStatus      SCDNotifierInformViaFD          (SCDSessionRef          session,
+                                                int32_t                identifier,
+                                                int                    *fd);
+
+/*!
+       @function SCDNotifierInformViaSignal
+       @discussion Requests that the specified BSD signal be sent to the process
+               with the indicated process id whenever a change has been detected
+               to one of the system configuration data entries associated with the
+               current sessions notifier keys.
+
+               Note: this function is not valid for "configd" plug-ins.
+
+       @param session Pass a SCDSessionRef handle which should be used for
+               communication with the server.
+       @param pid Pass a UNIX proces ID which should be signalled for any notifications.
+       @param sig Pass a signal number to be used.
+       @result A constant of type SCDStatus indicating the success (or failure) of
+               the call.
+ */
+SCDStatus      SCDNotifierInformViaSignal      (SCDSessionRef          session,
+                                                pid_t                  pid,
+                                                int                    sig);
+
+/*!
+       @function SCDNotifierCancel
+       @discussion Cancels all outstanding notification delivery request for this
+               session.
+
+       @param session Pass a SCDSessionRef handle which should be used for
+               communication with the server.
+       @result A constant of type SCDStatus indicating the success (or failure) of
+               the call.
+ */
+SCDStatus      SCDNotifierCancel               (SCDSessionRef          session);
+
+/*!
+       @function SCDOptionGet
+       @discussion Returns the value associated with the specified option.
+       @param session Pass a SCDSessionRef handle of the option of interest (or
+               NULL for the global option setting).
+       @param option Pass the SCDOption of interest.
+       @result The current value of the specified option.
+ */
+int            SCDOptionGet                    (SCDSessionRef          session,
+                                                int                    option);
+
+/*!
+       @function SCDOptionSet
+       @discussion Sets the value associated with the specified option.
+       @param session Pass a SCDSessionRef handle for the option to be set (or
+               NULL for the global option settings).
+       @param option Pass the SCDOption to be updated.
+       @param value Pass the new value for the option.
+       @result The current value of the specified option.
+ */
+void           SCDOptionSet                    (SCDSessionRef          session,
+                                                int                    option,
+                                                int                    value);
+
+/*!
+       @function SCDSessionLog
+       @discussion Issues a log and/or debug message.
+       @param session Pass a SCDSessionRef handle for the current session..
+       @param level Pass a syslog(3) logging priority.
+
+       Note: LOG_DEBUG messages will not be issued if the verbose option has not been enabled.
+       @param formatString Pass the CF format string
+       @result The specified message will be logged.
+ */
+void           SCDSessionLog                   (SCDSessionRef          session,
+                                                int                    level,
+                                                CFStringRef            formatString,
+                                                ...);
+
+/*!
+       @function SCDLog
+       @discussion Issues a log and/or debug message.
+       @param level Pass a syslog(3) logging priority.
+       @param formatString Pass the CF format string
+       @result The specified message will be logged.
+ */
+void           SCDLog                          (int                    level,
+                                                CFStringRef            formatString,
+                                                ...);
+
+const char *   SCDError                        (SCDStatus              status);
+
+__END_DECLS
+
+#endif /* _SCD_H */
diff --git a/SystemConfiguration.fproj/SCDAdd.c b/SystemConfiguration.fproj/SCDAdd.c
new file mode 100644 (file)
index 0000000..382249e
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include <mach/mach.h>
+#include <mach/mach_error.h>
+
+#include <SystemConfiguration/SCD.h>
+#include "config.h"            /* MiG generated file */
+#include "SCDPrivate.h"
+
+
+SCDStatus
+SCDAdd(SCDSessionRef session, CFStringRef key, SCDHandleRef handle)
+{
+       SCDSessionPrivateRef    sessionPrivate = (SCDSessionPrivateRef)session;
+       kern_return_t           status;
+       CFDataRef               xmlKey;         /* serialized key */
+       xmlData_t               myKeyRef;
+       CFIndex                 myKeyLen;
+       CFDataRef               xmlData;        /* serialized data */
+       xmlData_t               myDataRef;
+       CFIndex                 myDataLen;
+       int                     newInstance;
+       SCDStatus               scd_status;
+
+       SCDLog(LOG_DEBUG, CFSTR("SCDAdd:"));
+       SCDLog(LOG_DEBUG, CFSTR("  key          = %@"), key);
+       SCDLog(LOG_DEBUG, CFSTR("  data         = %@"), SCDHandleGetData(handle));
+
+       if (key == NULL) {
+               return SCD_INVALIDARGUMENT;     /* no key specified */
+       }
+
+       if ((session == NULL) || (sessionPrivate->server == MACH_PORT_NULL)) {
+               return SCD_NOSESSION;           /* you can't do anything with a closed session */
+       }
+
+       /* serialize the key and data */
+       xmlKey = CFPropertyListCreateXMLData(NULL, key);
+       myKeyRef = (xmlData_t)CFDataGetBytePtr(xmlKey);
+       myKeyLen = CFDataGetLength(xmlKey);
+
+       xmlData = CFPropertyListCreateXMLData(NULL, SCDHandleGetData(handle));
+       myDataRef = (xmlData_t)CFDataGetBytePtr(xmlData);
+       myDataLen = CFDataGetLength(xmlData);
+
+       /* send the key & data to the server */
+       status = configadd(sessionPrivate->server,
+                          myKeyRef,
+                          myKeyLen,
+                          myDataRef,
+                          myDataLen,
+                          &newInstance,
+                          (int *)&scd_status);
+
+       /* clean up */
+       CFRelease(xmlKey);
+       CFRelease(xmlData);
+
+       if (status != KERN_SUCCESS) {
+               if (status != MACH_SEND_INVALID_DEST)
+                       SCDLog(LOG_DEBUG, CFSTR("configadd(): %s"), mach_error_string(status));
+               (void) mach_port_destroy(mach_task_self(), sessionPrivate->server);
+               sessionPrivate->server = MACH_PORT_NULL;
+               return SCD_NOSERVER;
+       }
+
+       if (scd_status == SCD_OK) {
+               _SCDHandleSetInstance(handle, newInstance);
+       }
+
+       SCDLog(LOG_DEBUG, CFSTR("  new instance = %d"), SCDHandleGetInstance(handle));
+
+       return scd_status;
+}
diff --git a/SystemConfiguration.fproj/SCDAddSession.c b/SystemConfiguration.fproj/SCDAddSession.c
new file mode 100644 (file)
index 0000000..b19ae0e
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include <mach/mach.h>
+#include <mach/mach_error.h>
+
+#include <SystemConfiguration/SCD.h>
+#include "config.h"            /* MiG generated file */
+#include "SCDPrivate.h"
+
+
+SCDStatus
+SCDAddSession(SCDSessionRef session, CFStringRef key, SCDHandleRef handle)
+{
+       SCDSessionPrivateRef    sessionPrivate = (SCDSessionPrivateRef)session;
+       kern_return_t           status;
+       CFDataRef               xmlKey;         /* serialized key */
+       xmlData_t               myKeyRef;
+       CFIndex                 myKeyLen;
+       CFDataRef               xmlData;        /* serialized data */
+       xmlData_t               myDataRef;
+       CFIndex                 myDataLen;
+       int                     newInstance;
+       SCDStatus               scd_status;
+
+       SCDLog(LOG_DEBUG, CFSTR("SCDAddSession:"));
+       SCDLog(LOG_DEBUG, CFSTR("  key          = %@"), key);
+       SCDLog(LOG_DEBUG, CFSTR("  data         = %@"), SCDHandleGetData(handle));
+
+       if (key == NULL) {
+               return SCD_INVALIDARGUMENT;     /* no key specified */
+       }
+
+       if ((session == NULL) || (sessionPrivate->server == MACH_PORT_NULL)) {
+               return SCD_NOSESSION;           /* you can't do anything with a closed session */
+       }
+
+       /* serialize the key and data */
+       xmlKey = CFPropertyListCreateXMLData(NULL, key);
+       myKeyRef = (xmlData_t)CFDataGetBytePtr(xmlKey);
+       myKeyLen = CFDataGetLength(xmlKey);
+
+       xmlData = CFPropertyListCreateXMLData(NULL, SCDHandleGetData(handle));
+       myDataRef = (xmlData_t)CFDataGetBytePtr(xmlData);
+       myDataLen = CFDataGetLength(xmlData);
+
+       /* send the key & data to the server */
+       status = configadd_s(sessionPrivate->server,
+                           myKeyRef,
+                           myKeyLen,
+                           myDataRef,
+                           myDataLen,
+                           &newInstance,
+                           (int *)&scd_status);
+
+       /* clean up */
+       CFRelease(xmlKey);
+       CFRelease(xmlData);
+
+       if (status != KERN_SUCCESS) {
+               if (status != MACH_SEND_INVALID_DEST)
+                       SCDLog(LOG_DEBUG, CFSTR("configadd_s(): %s"), mach_error_string(status));
+               (void) mach_port_destroy(mach_task_self(), sessionPrivate->server);
+               sessionPrivate->server = MACH_PORT_NULL;
+               return SCD_NOSERVER;
+       }
+
+       if (scd_status == SCD_OK) {
+               _SCDHandleSetInstance(handle, newInstance);
+       }
+
+       SCDLog(LOG_DEBUG, CFSTR("  new instance = %d"), SCDHandleGetInstance(handle));
+
+       return scd_status;
+}
diff --git a/SystemConfiguration.fproj/SCDClose.c b/SystemConfiguration.fproj/SCDClose.c
new file mode 100644 (file)
index 0000000..86aeb3c
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include <unistd.h>
+#include <mach/mach.h>
+#include <mach/mach_error.h>
+
+#include <SystemConfiguration/SCD.h>
+#include "config.h"            /* MiG generated file */
+#include "SCDPrivate.h"
+
+
+SCDStatus
+SCDClose(SCDSessionRef *session)
+{
+       SCDSessionPrivateRef    sessionPrivate;
+       int                     oldThreadState;
+       kern_return_t           status;
+       SCDStatus               scd_status;
+       CFIndex                 keyCnt;
+
+       SCDLog(LOG_DEBUG, CFSTR("SCDClose:"));
+
+       if ((session == NULL) || (*session == NULL)) {
+               return SCD_NOSESSION;           /* you can't do anything with a closed session */
+       }
+       sessionPrivate = (SCDSessionPrivateRef)*session;
+
+       (void) pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldThreadState);
+
+       scd_status = (sessionPrivate->server == MACH_PORT_NULL) ? SCD_NOSESSION : SCD_OK;
+
+       /* Remove notification keys */
+       if ((keyCnt = CFSetGetCount(sessionPrivate->keys)) > 0) {
+               void            **watchedKeys;
+               CFArrayRef      keysToRemove;
+               CFIndex i;
+
+               watchedKeys = CFAllocatorAllocate(NULL, keyCnt * sizeof(CFStringRef), 0);
+               CFSetGetValues(sessionPrivate->keys, watchedKeys);
+               keysToRemove = CFArrayCreate(NULL, watchedKeys, keyCnt, &kCFTypeArrayCallBacks);
+               CFAllocatorDeallocate(NULL, watchedKeys);
+               for (i=0; i<keyCnt; i++) {
+                       if (scd_status == SCD_OK) {
+                               scd_status = SCDNotifierRemove(*session,
+                                                              CFArrayGetValueAtIndex(keysToRemove, i),
+                                                              0);
+                       }
+               }
+               CFRelease(keysToRemove);
+       }
+
+       /* Remove regex notification keys */
+       if ((keyCnt = CFSetGetCount(sessionPrivate->reKeys)) > 0) {
+               void            **watchedKeys;
+               CFArrayRef      keysToRemove;
+               CFIndex i;
+
+               watchedKeys = CFAllocatorAllocate(NULL, keyCnt * sizeof(CFStringRef), 0);
+               CFSetGetValues(sessionPrivate->reKeys, watchedKeys);
+               keysToRemove = CFArrayCreate(NULL, watchedKeys, keyCnt, &kCFTypeArrayCallBacks);
+               CFAllocatorDeallocate(NULL, watchedKeys);
+               for (i=0; i<keyCnt; i++) {
+                       if (scd_status == SCD_OK) {
+                               scd_status = SCDNotifierRemove(*session,
+                                                              CFArrayGetValueAtIndex(keysToRemove, i),
+                                                              kSCDRegexKey);
+                       }
+               }
+               CFRelease(keysToRemove);
+       }
+
+       /* Remove/cancel any outstanding notification requests. */
+       (void) SCDNotifierCancel(*session);
+
+       if (SCDOptionGet(*session, kSCDOptionIsLocked) && (scd_status == SCD_OK)) {
+               scd_status = SCDUnlock(*session);       /* release the lock */
+       }
+
+       if (scd_status == SCD_OK) {
+               status = configclose(sessionPrivate->server, (int *)&scd_status);
+               if (status != KERN_SUCCESS) {
+                       if (status != MACH_SEND_INVALID_DEST)
+                               SCDLog(LOG_DEBUG, CFSTR("configclose(): %s"), mach_error_string(status));
+                       scd_status = SCD_NOSERVER;
+               }
+
+               (void) mach_port_destroy(mach_task_self(), sessionPrivate->server);
+               sessionPrivate->server = MACH_PORT_NULL;
+       }
+
+       CFAllocatorDeallocate(NULL, sessionPrivate);
+       *session = NULL;
+
+       (void) pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldThreadState);
+       pthread_testcancel();
+
+       return scd_status;
+}
diff --git a/SystemConfiguration.fproj/SCDConsoleUser.c b/SystemConfiguration.fproj/SCDConsoleUser.c
new file mode 100644 (file)
index 0000000..a607b93
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include <SystemConfiguration/SystemConfiguration.h>
+
+
+CFStringRef
+SCDKeyCreateConsoleUser()
+{
+       return SCDKeyCreate(CFSTR("%@/%@/%@"),
+                           CFSTR("state:"),    // FIXME!!! (should be kSCCacheDomainState)
+                           kSCCompUsers,
+                           kSCEntUsersConsoleUser);
+}
+
+
+SCDStatus
+SCDConsoleUserGet(char *user, int userlen, uid_t *uid, gid_t *gid)
+{
+       CFDictionaryRef dict;
+       SCDHandleRef    handle  = NULL;
+       CFStringRef     key;
+       SCDSessionRef   session = NULL;
+       SCDStatus       status;
+
+       /* get current user */
+       status = SCDOpen(&session, CFSTR("SCDConsoleUserGet"));
+       if (status != SCD_OK) {
+               goto done;
+       }
+
+       key = SCDKeyCreateConsoleUser();
+       status = SCDGet(session, key, &handle);
+       CFRelease(key);
+       if (status != SCD_OK) {
+               goto done;
+       }
+
+       dict = SCDHandleGetData(handle);
+
+       if (user && (userlen > 0)) {
+               CFStringRef     consoleUser;
+
+               bzero(user, userlen);
+               if (CFDictionaryGetValueIfPresent(dict,
+                                                 kSCPropUsersConsoleUserName,
+                                                 (void **)&consoleUser)) {
+                       CFIndex         len;
+                       CFRange         range;
+
+                       range = CFRangeMake(0, CFStringGetLength(consoleUser));
+                       (void)CFStringGetBytes(consoleUser,
+                                              range,
+                                              kCFStringEncodingMacRoman,
+                                              0,
+                                              FALSE,
+                                              user,
+                                              userlen,
+                                              &len);
+               }
+       }
+
+       if (uid) {
+               CFNumberRef     num;
+               SInt32          val;
+
+               if (CFDictionaryGetValueIfPresent(dict,
+                                                 kSCPropUsersConsoleUserUID,
+                                                 (void **)&num)) {
+                       if (CFNumberGetValue(num, kCFNumberSInt32Type, &val)) {
+                               *uid = (uid_t)val;
+                       }
+               }
+       }
+
+       if (gid) {
+               CFNumberRef     num;
+               SInt32          val;
+
+               if (CFDictionaryGetValueIfPresent(dict,
+                                                 kSCPropUsersConsoleUserGID,
+                                                 (void **)&num)) {
+                       if (CFNumberGetValue(num, kCFNumberSInt32Type, &val)) {
+                               *gid = (gid_t)val;
+                       }
+               }
+       }
+
+    done :
+
+       if (handle)     SCDHandleRelease(handle);
+       if (session)    (void) SCDClose(&session);
+       return status;
+}
+
+
+SCDStatus
+SCDConsoleUserSet(const char *user, uid_t uid, gid_t gid)
+{
+       CFStringRef             consoleUser;
+       CFMutableDictionaryRef  dict    = NULL;
+       SCDHandleRef            handle  = NULL;
+       CFStringRef             key     = SCDKeyCreateConsoleUser();
+       CFNumberRef             num;
+       SCDSessionRef           session = NULL;
+       SCDStatus               status;
+
+       status = SCDOpen(&session, CFSTR("SCDConsoleUserSet"));
+       if (status != SCD_OK) {
+               goto done;
+       }
+
+       if (user == NULL) {
+               (void)SCDRemove(session, key);
+               goto done;
+       }
+
+       dict = CFDictionaryCreateMutable(NULL,
+                                        0,
+                                        &kCFTypeDictionaryKeyCallBacks,
+                                        &kCFTypeDictionaryValueCallBacks);
+
+       consoleUser = CFStringCreateWithCString(NULL, user, kCFStringEncodingMacRoman);
+       CFDictionarySetValue(dict, kSCPropUsersConsoleUserName, consoleUser);
+       CFRelease(consoleUser);
+
+       num = CFNumberCreate(NULL, kCFNumberSInt32Type, (SInt32 *)&uid);
+       CFDictionarySetValue(dict, kSCPropUsersConsoleUserUID, num);
+       CFRelease(num);
+
+       num = CFNumberCreate(NULL, kCFNumberSInt32Type, (SInt32 *)&gid);
+       CFDictionarySetValue(dict, kSCPropUsersConsoleUserGID, num);
+       CFRelease(num);
+
+       handle = SCDHandleInit();
+       SCDHandleSetData(handle, dict);
+
+       status = SCDLock(session);
+       if (status != SCD_OK) {
+               goto done;
+       }
+       (void)SCDRemove(session, key);
+       (void)SCDAdd   (session, key, handle);
+       status = SCDUnlock(session);
+
+    done :
+
+       if (dict)       CFRelease(dict);
+       if (handle)     SCDHandleRelease(handle);
+       if (key)        CFRelease(key);
+       if (session)    (void) SCDClose(&session);
+       return status;
+}
diff --git a/SystemConfiguration.fproj/SCDConsoleUser.h b/SystemConfiguration.fproj/SCDConsoleUser.h
new file mode 100644 (file)
index 0000000..e164799
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#ifndef _SCDCONSOLEUSER_H
+#define _SCDCONSOLEUSER_H
+
+#include <sys/cdefs.h>
+
+/*!
+       @header SCDConsoleUser.h
+       The SystemConfiguration framework provides access to the data used
+               to configure a running system.
+
+       Specifically, the SCDConsoleUserXXX() API's allow an application
+               to determine (or set) the login/user currently using the
+               console.
+
+       The APIs provided by this framework communicate with the "configd"
+               daemon to obtain information regarding the systems current
+               configuration.
+ */
+
+
+__BEGIN_DECLS
+
+/*!
+       @function SCDKeyCreateConsoleUser
+       @discussion Creates a key which can be used by the SCDNotifierAdd()
+               function to receive notifications when the current "Console"
+               user changes.
+       @result A notification string for the current "Console" user.
+*/
+CFStringRef    SCDKeyCreateConsoleUser ();
+
+/*!
+       @function SCDConsoleUserGet
+       @discussion Gets the name, user ID, and group ID of the currently
+               logged in user.
+       @param user A pointer to a character buffer of at least size
+               userlen. The returned name is null-terminated unless
+               in-sufficient space is provided.If NULL, this value
+               will not be returned.
+       @param userlen Pass an integer specifying the maximum size of the
+               user buffer.
+       @param uid A pointer to memory which will be filled with the user ID
+               of the current "Console" user. If NULL, this value will not
+               be returned.
+       @param gid A pointer to memory which will be filled with the group ID
+               of the current "Console" user. If NULL, this value will not be
+               returned.
+       @result A constant of type SCDStatus indicating the success (or failure) of
+               the call.
+ */
+SCDStatus      SCDConsoleUserGet       (char           *user,
+                                        int            userlen,
+                                        uid_t          *uid,
+                                        gid_t          *gid);
+
+/*!
+       @function SCDConsoleUserSet
+       @discussion Sets the name, user ID, and group ID of the currently
+               logged in user.
+       @param user A pointer to a character buffer containing the name of
+               the current "Console" user. If NULL, any current "Console"
+               user information will be reset.
+       @param uid The user ID of the current "Console" user.
+       @param gid The group ID of the current "Console" user.
+       @result A constant of type SCDStatus indicating the success (or failure) of
+        the call.
+ */
+SCDStatus      SCDConsoleUserSet       (const char     *user,
+                                        uid_t          uid,
+                                        gid_t          gid);
+
+__END_DECLS
+
+#endif /* _SCDCONSOLEUSER_H */
diff --git a/SystemConfiguration.fproj/SCDGet.c b/SystemConfiguration.fproj/SCDGet.c
new file mode 100644 (file)
index 0000000..d59a46a
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include <mach/mach.h>
+#include <mach/mach_error.h>
+
+#include <SystemConfiguration/SCD.h>
+#include "config.h"            /* MiG generated file */
+#include "SCDPrivate.h"
+
+
+SCDStatus
+SCDGet(SCDSessionRef session, CFStringRef key, SCDHandleRef *handle)
+{
+       SCDSessionPrivateRef    sessionPrivate = (SCDSessionPrivateRef)session;
+       kern_return_t           status;
+       CFDataRef               xmlKey;         /* key (XML serialized) */
+       xmlData_t               myKeyRef;       /* key (serialized) */
+       CFIndex                 myKeyLen;
+       xmlDataOut_t            xmlDataRef;     /* data (serialized) */
+       CFIndex                 xmlDataLen;
+       CFDataRef               xmlData;        /* data (XML serialized) */
+       CFPropertyListRef       data;           /* data (un-serialized) */
+       int                     newInstance;
+       SCDStatus               scd_status;
+       CFStringRef             xmlError;
+
+       SCDLog(LOG_DEBUG, CFSTR("SCDGet:"));
+       SCDLog(LOG_DEBUG, CFSTR("  key      = %@"), key);
+
+       if (key == NULL) {
+               return SCD_INVALIDARGUMENT;     /* no key specified */
+       }
+
+       if ((session == NULL) || (sessionPrivate->server == MACH_PORT_NULL)) {
+               return SCD_NOSESSION;
+       }
+
+       /* serialize the key */
+       xmlKey = CFPropertyListCreateXMLData(NULL, key);
+       myKeyRef = (xmlData_t)CFDataGetBytePtr(xmlKey);
+       myKeyLen = CFDataGetLength(xmlKey);
+
+       /* send the key & fetch the associated data from the server */
+       status = configget(sessionPrivate->server,
+                          myKeyRef,
+                          myKeyLen,
+                          &xmlDataRef,
+                          (int *)&xmlDataLen,
+                          &newInstance,
+                          (int *)&scd_status);
+
+       /* clean up */
+       CFRelease(xmlKey);
+
+       if (status != KERN_SUCCESS) {
+               if (status != MACH_SEND_INVALID_DEST)
+                       SCDLog(LOG_DEBUG, CFSTR("configget(): %s"), mach_error_string(status));
+               (void) mach_port_destroy(mach_task_self(), sessionPrivate->server);
+               sessionPrivate->server = MACH_PORT_NULL;
+               return SCD_NOSERVER;
+       }
+
+       if (scd_status != SCD_OK) {
+               status = vm_deallocate(mach_task_self(), (vm_address_t)xmlDataRef, xmlDataLen);
+               if (status != KERN_SUCCESS) {
+                       SCDLog(LOG_DEBUG, CFSTR("vm_deallocate(): %s"), mach_error_string(status));
+                       /* non-fatal???, proceed */
+               }
+               *handle = NULL;
+               return scd_status;
+       }
+
+       /* un-serialize the data, return a handle associated with the key */
+       *handle = SCDHandleInit();
+       xmlData = CFDataCreate(NULL, xmlDataRef, xmlDataLen);
+       status = vm_deallocate(mach_task_self(), (vm_address_t)xmlDataRef, xmlDataLen);
+       if (status != KERN_SUCCESS) {
+               SCDLog(LOG_DEBUG, CFSTR("vm_deallocate(): %s"), mach_error_string(status));
+               /* non-fatal???, proceed */
+       }
+       data = CFPropertyListCreateFromXMLData(NULL,
+                                              xmlData,
+                                              kCFPropertyListImmutable,
+                                              &xmlError);
+       CFRelease(xmlData);
+       if (xmlError) {
+               SCDLog(LOG_DEBUG, CFSTR("CFPropertyListCreateFromXMLData() data: %s"), xmlError);
+               SCDHandleRelease(*handle);
+               return SCD_FAILED;
+       }
+       SCDHandleSetData(*handle, data);
+       CFRelease(data);
+
+       _SCDHandleSetInstance(*handle, newInstance);
+
+       SCDLog(LOG_DEBUG, CFSTR("  data     = %@"), SCDHandleGetData(*handle));
+       SCDLog(LOG_DEBUG, CFSTR("  instance = %d"), SCDHandleGetInstance(*handle));
+
+       return scd_status;
+}
diff --git a/SystemConfiguration.fproj/SCDHandle.c b/SystemConfiguration.fproj/SCDHandle.c
new file mode 100644 (file)
index 0000000..5e4ab8e
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include <SystemConfiguration/SCD.h>
+#include "SCDPrivate.h"
+
+
+SCDHandleRef
+SCDHandleInit()
+{
+       SCDHandlePrivateRef privateHandle = CFAllocatorAllocate(NULL, sizeof(SCDHandlePrivate), 0);
+
+       /* set data */
+       privateHandle->data = NULL;
+
+       /* set instance */
+       privateHandle->instance = 0;
+
+       return (SCDHandleRef)privateHandle;
+}
+
+
+void
+SCDHandleRelease(SCDHandleRef handle)
+{
+       SCDHandlePrivateRef privateHandle = (SCDHandlePrivateRef)handle;
+
+       if (privateHandle->data)
+               CFRelease(privateHandle->data);
+
+       CFAllocatorDeallocate(NULL, privateHandle);
+       return;
+}
+
+
+int
+SCDHandleGetInstance(SCDHandleRef handle)
+{
+       SCDHandlePrivateRef privateHandle = (SCDHandlePrivateRef)handle;
+
+       return privateHandle->instance;
+}
+
+
+void
+_SCDHandleSetInstance(SCDHandleRef handle, int instance)
+{
+       SCDHandlePrivateRef privateHandle = (SCDHandlePrivateRef)handle;
+
+       privateHandle->instance = instance;
+       return;
+}
+
+
+CFPropertyListRef
+SCDHandleGetData(SCDHandleRef handle)
+{
+       SCDHandlePrivateRef privateHandle = (SCDHandlePrivateRef)handle;
+
+       if (privateHandle->data == NULL) {
+               return CFSTR("SCDHandleRef not initialized.");
+       }
+
+       return privateHandle->data;
+}
+
+
+void
+SCDHandleSetData(SCDHandleRef handle, CFPropertyListRef data)
+{
+       SCDHandlePrivateRef privateHandle = (SCDHandlePrivateRef)handle;
+
+       /* remove reference to data previously associated with handle */
+       if (privateHandle->data)
+               CFRelease(privateHandle->data);
+
+       /* associate new data with handle, keep a reference as needed */
+       privateHandle->data = data;
+       if (privateHandle->data)
+               CFRetain(privateHandle->data);
+
+       return;
+}
diff --git a/SystemConfiguration.fproj/SCDHostName.c b/SystemConfiguration.fproj/SCDHostName.c
new file mode 100644 (file)
index 0000000..c127f7f
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include <SystemConfiguration/SystemConfiguration.h>
+
+
+CFStringRef
+SCDKeyCreateHostName()
+{
+       return SCDKeyCreate(CFSTR("%@/%@"),
+                           kSCCacheDomainSetup,
+                           kSCCompSystem);
+}
+
+
+SCDStatus
+SCDHostNameGet(CFStringRef *name, CFStringEncoding *nameEncoding)
+{
+       CFDictionaryRef dict;
+       SCDHandleRef    handle  = NULL;
+       CFStringRef     key;
+       SCDSessionRef   session = NULL;
+       SCDStatus       status;
+
+       if (name == NULL) {
+               return SCD_FAILED;
+       }
+
+       /* get current user */
+       status = SCDOpen(&session, CFSTR("SCDHostNameGet"));
+       if (status != SCD_OK) {
+               goto done;
+       }
+
+       key = SCDKeyCreateHostName();
+       status = SCDGet(session, key, &handle);
+       CFRelease(key);
+       if (status != SCD_OK) {
+               goto done;
+       }
+
+       dict = SCDHandleGetData(handle);
+
+       *name = CFDictionaryGetValue(dict, kSCPropSystemComputerName);
+       if (*name == NULL) {
+               goto done;
+       }
+       CFRetain(*name);
+
+       if (nameEncoding) {
+               CFNumberRef     num;
+
+               num = CFDictionaryGetValue(dict,
+                                          kSCPropSystemComputerNameEncoding);
+               if (num) {
+                       CFNumberGetValue(num, kCFNumberIntType, nameEncoding);
+               } else {
+                       *nameEncoding = CFStringGetSystemEncoding();
+               }
+       }
+
+    done :
+
+       if (handle)     SCDHandleRelease(handle);
+       if (session)    (void) SCDClose(&session);
+       return status;
+}
diff --git a/SystemConfiguration.fproj/SCDHostName.h b/SystemConfiguration.fproj/SCDHostName.h
new file mode 100644 (file)
index 0000000..2ce1cad
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#ifndef _SCDHOSTNAME_H
+#define _SCDHOSTNAME_H
+
+#include <sys/cdefs.h>
+
+/*!
+       @header SCDHostName.h
+       The SystemConfiguration framework provides access to the data used
+               to configure a running system.
+
+       Specifically, the SCDHostNameXXX() API's allow an application
+               to determine (or set) the login/user currently using the
+               console.
+
+       The APIs provided by this framework communicate with the "configd"
+               daemon to obtain information regarding the systems current
+               configuration.
+ */
+
+
+__BEGIN_DECLS
+
+/*!
+       @function SCDKeyCreateHostName
+       @discussion Creates a key which can be used by the SCDNotifierAdd()
+               function to receive notifications when the current
+               computer/host name changes.
+       @result A notification string for the current computer/host name".
+*/
+CFStringRef    SCDKeyCreateHostName    ();
+
+/*!
+       @function SCDHostNameGet
+       @discussion Gets the current computer/host name.
+       @param name A pointer to memory which will be filled with the current
+               computer/host name.
+       @param nameEncoding A pointer to memory which, if non-NULL, will be
+               filled with the encoding associated with the computer/host name.
+       @result A constant of type SCDStatus indicating the success (or failure) of
+               the call.
+ */
+SCDStatus      SCDHostNameGet          (CFStringRef            *name,
+                                        CFStringEncoding       *nameEncoding);
+
+__END_DECLS
+
+#endif /* _SCDHOSTNAME_H */
diff --git a/SystemConfiguration.fproj/SCDKeys.c b/SystemConfiguration.fproj/SCDKeys.c
new file mode 100644 (file)
index 0000000..e2a51c7
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include <SystemConfiguration/SystemConfiguration.h>
+
+#include <stdarg.h>
+
+/*
+ * SCDKeyCreate*
+ * - convenience routines that create a CFString key for an item in the cache
+ */
+
+/*
+ * Function: SCDKeyCreate
+ * Purpose:
+ *    Creates a cache key using the given format.
+ */
+CFStringRef
+SCDKeyCreate(CFStringRef fmt, ...)
+{
+       va_list args;
+       va_start(args, fmt);
+       return (CFStringCreateWithFormatAndArguments(NULL,
+                                                    NULL,
+                                                    fmt,
+                                                    args));
+}
+
+CFStringRef
+SCDKeyCreateNetworkGlobalEntity(CFStringRef domain, CFStringRef entity)
+{
+       return (CFStringCreateWithFormat(NULL,
+                                        NULL,
+                                        CFSTR("%@/%@/%@/%@"),
+                                        domain,
+                                        kSCCompNetwork,
+                                        kSCCompGlobal,
+                                        entity));
+}
+
+CFStringRef
+SCDKeyCreateNetworkInterface(CFStringRef domain)
+{
+       return (CFStringCreateWithFormat(NULL,
+                                        NULL,
+                                        CFSTR("%@/%@/%@"),
+                                        domain,
+                                        kSCCompNetwork,
+                                        kSCCompInterface));
+}
+
+CFStringRef
+SCDKeyCreateNetworkInterfaceEntity(CFStringRef domain,
+                                  CFStringRef ifname,
+                                  CFStringRef entity)
+{
+       if (entity == NULL) {
+               return (CFStringCreateWithFormat(NULL,
+                                                NULL,
+                                                CFSTR("%@/%@/%@/%@"),
+                                                domain,
+                                                kSCCompNetwork,
+                                                kSCCompInterface,
+                                                ifname));
+       } else {
+               return (CFStringCreateWithFormat(NULL,
+                                                NULL,
+                                                CFSTR("%@/%@/%@/%@/%@"),
+                                                domain,
+                                                kSCCompNetwork,
+                                                kSCCompInterface,
+                                                ifname,
+                                                entity));
+       }
+}
+
+CFStringRef
+SCDKeyCreateNetworkServiceEntity(CFStringRef domain,
+                                CFStringRef serviceID,
+                                CFStringRef entity)
+{
+       if (entity == NULL) {
+               return (CFStringCreateWithFormat(NULL,
+                                                NULL,
+                                                CFSTR("%@/%@/%@/%@"),
+                                                domain,
+                                                kSCCompNetwork,
+                                                kSCCompService,
+                                                serviceID));
+       } else {
+               return (CFStringCreateWithFormat(NULL,
+                                                NULL,
+                                                CFSTR("%@/%@/%@/%@/%@"),
+                                                domain,
+                                                kSCCompNetwork,
+                                                kSCCompService,
+                                                serviceID,
+                                                entity));
+       }
+}
diff --git a/SystemConfiguration.fproj/SCDKeys.h b/SystemConfiguration.fproj/SCDKeys.h
new file mode 100644 (file)
index 0000000..4c1f2b6
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+/*
+ * SCDKeys.h
+ */
+
+#ifndef _SCDKEYS_H
+#define _SCDKEYS_H
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+/*
+ * SCDKeyCreate*
+ * - convenience routines that create a CFString key for an item in the cache
+ */
+
+/*
+ * Function: SCDKeyCreate
+ * Purpose:
+ *    Creates a cache key using the given format.
+ */
+CFStringRef    SCDKeyCreate                            (CFStringRef    fmt,
+                                                        ...);
+
+CFStringRef    SCDKeyCreateNetworkGlobalEntity         (CFStringRef    domain,
+                                                        CFStringRef    entity);
+
+CFStringRef    SCDKeyCreateNetworkInterface            (CFStringRef    domain);
+
+CFStringRef    SCDKeyCreateNetworkInterfaceEntity      (CFStringRef    domain,
+                                                        CFStringRef    ifname,
+                                                        CFStringRef    entity);
+
+CFStringRef    SCDKeyCreateNetworkServiceEntity        (CFStringRef    domain,
+                                                        CFStringRef    serviceID,
+                                                        CFStringRef    entity);
+
+__END_DECLS
+
+#endif /* _SCDKEYS_H */
diff --git a/SystemConfiguration.fproj/SCDList.c b/SystemConfiguration.fproj/SCDList.c
new file mode 100644 (file)
index 0000000..1efabf3
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include <sys/types.h>
+#include <regex.h>
+
+#include <mach/mach.h>
+#include <mach/mach_error.h>
+
+#include <SystemConfiguration/SCD.h>
+#include "config.h"            /* MiG generated file */
+#include "SCDPrivate.h"
+
+
+static CFComparisonResult
+sort_keys(const void *p1, const void *p2, void *context) {
+       CFStringRef key1 = (CFStringRef)p1;
+       CFStringRef key2 = (CFStringRef)p2;
+       return CFStringCompare(key1, key2, 0);
+}
+
+
+SCDStatus
+SCDList(SCDSessionRef session, CFStringRef key, int regexOptions, CFArrayRef *subKeys)
+{
+       SCDSessionPrivateRef    sessionPrivate = (SCDSessionPrivateRef)session;
+       kern_return_t           status;
+       CFDataRef               xmlKey;         /* serialized key */
+       xmlData_t               myKeyRef;
+       CFIndex                 myKeyLen;
+       CFDataRef               xmlData;        /* data (XML serialized) */
+       xmlDataOut_t            xmlDataRef;     /* serialized data */
+       int                     xmlDataLen;
+       SCDStatus               scd_status;
+       CFArrayRef              allKeys;
+       CFMutableArrayRef       sortedKeys;
+       CFStringRef             xmlError;
+
+       SCDLog(LOG_DEBUG, CFSTR("SCDList:"));
+       SCDLog(LOG_DEBUG, CFSTR("  key          = %@"), key);
+       SCDLog(LOG_DEBUG, CFSTR("  regexOptions = %0o"), regexOptions);
+
+       if (key == NULL) {
+               return SCD_INVALIDARGUMENT;     /* no key specified */
+       }
+
+       if ((session == NULL) || (sessionPrivate->server == MACH_PORT_NULL)) {
+               return SCD_NOSESSION;
+       }
+
+       /* serialize the key */
+       xmlKey = CFPropertyListCreateXMLData(NULL, key);
+       myKeyRef = (xmlData_t)CFDataGetBytePtr(xmlKey);
+       myKeyLen = CFDataGetLength(xmlKey);
+
+       /* send the key & fetch the associated data from the server */
+       status = configlist(sessionPrivate->server,
+                           myKeyRef,
+                           myKeyLen,
+                           regexOptions,
+                           &xmlDataRef,
+                           &xmlDataLen,
+                           (int *)&scd_status);
+
+       /* clean up */
+       CFRelease(xmlKey);
+
+       if (status != KERN_SUCCESS) {
+               if (status != MACH_SEND_INVALID_DEST)
+                       SCDLog(LOG_DEBUG, CFSTR("configlist(): %s"), mach_error_string(status));
+               (void) mach_port_destroy(mach_task_self(), sessionPrivate->server);
+               sessionPrivate->server = MACH_PORT_NULL;
+               return SCD_NOSERVER;
+       }
+
+       if (scd_status != SCD_OK) {
+               status = vm_deallocate(mach_task_self(), (vm_address_t)xmlDataRef, xmlDataLen);
+               if (status != KERN_SUCCESS) {
+                       SCDLog(LOG_DEBUG, CFSTR("vm_deallocate(): %s"), mach_error_string(status));
+                       /* non-fatal???, proceed */
+               }
+               *subKeys = NULL;
+               return scd_status;
+       }
+
+       /* un-serialize the list of keys */
+       xmlData = CFDataCreate(NULL, xmlDataRef, xmlDataLen);
+       status = vm_deallocate(mach_task_self(), (vm_address_t)xmlDataRef, xmlDataLen);
+       if (status != KERN_SUCCESS) {
+               SCDLog(LOG_DEBUG, CFSTR("vm_deallocate(): %s"), mach_error_string(status));
+               /* non-fatal???, proceed */
+       }
+       allKeys = CFPropertyListCreateFromXMLData(NULL,
+                                                 xmlData,
+                                                 kCFPropertyListImmutable,
+                                                 &xmlError);
+       CFRelease(xmlData);
+       if (xmlError) {
+               SCDLog(LOG_DEBUG, CFSTR("CFPropertyListCreateFromXMLData() list: %s"), xmlError);
+               return SCD_FAILED;
+       }
+
+       myKeyLen = CFArrayGetCount(allKeys);
+       sortedKeys = CFArrayCreateMutableCopy(NULL, myKeyLen, allKeys);
+       CFRelease(allKeys);
+       CFArraySortValues(sortedKeys,
+                         CFRangeMake(0, myKeyLen),
+                         sort_keys,
+                         NULL);
+
+       *subKeys = sortedKeys;
+       return scd_status;
+}
diff --git a/SystemConfiguration.fproj/SCDLock.c b/SystemConfiguration.fproj/SCDLock.c
new file mode 100644 (file)
index 0000000..ff1a1d4
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include <mach/mach.h>
+#include <mach/mach_error.h>
+
+#include <SystemConfiguration/SCD.h>
+#include "config.h"            /* MiG generated file */
+#include "SCDPrivate.h"
+
+
+SCDStatus
+SCDLock(SCDSessionRef session)
+{
+       SCDSessionPrivateRef    sessionPrivate = (SCDSessionPrivateRef)session;
+       kern_return_t           status;
+       SCDStatus               scd_status;
+
+       SCDLog(LOG_DEBUG, CFSTR("SCDLock:"));
+
+       if ((session == NULL) || (sessionPrivate->server == MACH_PORT_NULL)) {
+               return SCD_NOSESSION;   /* you must have an open session to play */
+       }
+
+       /* get the lock from the server */
+       status = configlock(sessionPrivate->server, (int *)&scd_status);
+
+       if (status != KERN_SUCCESS) {
+               if (status != MACH_SEND_INVALID_DEST)
+                       SCDLog(LOG_DEBUG, CFSTR("configlock(): %s"), mach_error_string(status));
+               (void) mach_port_destroy(mach_task_self(), sessionPrivate->server);
+               sessionPrivate->server = MACH_PORT_NULL;
+               return SCD_NOSERVER;
+       }
+
+       return scd_status;
+}
diff --git a/SystemConfiguration.fproj/SCDNotifierAdd.c b/SystemConfiguration.fproj/SCDNotifierAdd.c
new file mode 100644 (file)
index 0000000..689cb2e
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include <sys/types.h>
+#include <regex.h>
+
+#include <mach/mach.h>
+#include <mach/mach_error.h>
+
+#include <SystemConfiguration/SCD.h>
+#include "config.h"            /* MiG generated file */
+#include "SCDPrivate.h"
+
+
+SCDStatus
+SCDNotifierAdd(SCDSessionRef session, CFStringRef key, int regexOptions)
+{
+       SCDSessionPrivateRef    sessionPrivate = (SCDSessionPrivateRef)session;
+       kern_return_t           status;
+       CFDataRef               xmlKey;         /* serialized key */
+       xmlData_t               myKeyRef;
+       CFIndex                 myKeyLen;
+       SCDStatus               scd_status;
+
+       SCDLog(LOG_DEBUG, CFSTR("SCDNotifierAdd:"));
+       SCDLog(LOG_DEBUG, CFSTR("  key          = %@"), key);
+       SCDLog(LOG_DEBUG, CFSTR("  regexOptions = %0o"), regexOptions);
+
+       if (key == NULL) {
+               return SCD_INVALIDARGUMENT;     /* no key specified */
+       }
+
+       if ((session == NULL) || (sessionPrivate->server == MACH_PORT_NULL)) {
+               return SCD_NOSESSION;           /* you can't do anything with a closed session */
+       }
+
+       /*
+        * add new key after checking if key has already been defined
+        */
+       if (regexOptions & kSCDRegexKey) {
+               if (CFSetContainsValue(sessionPrivate->reKeys, key))
+                       return SCD_EXISTS;              /* sorry, key already exists in notifier list */
+               CFSetAddValue(sessionPrivate->reKeys, key);     /* add key to this sessions notifier list */
+       } else {
+               if (CFSetContainsValue(sessionPrivate->keys, key))
+                       return SCD_EXISTS;              /* sorry, key already exists in notifier list */
+               CFSetAddValue(sessionPrivate->keys, key);       /* add key to this sessions notifier list */
+       }
+
+       /* serialize the key */
+       xmlKey = CFPropertyListCreateXMLData(NULL, key);
+       myKeyRef = (xmlData_t)CFDataGetBytePtr(xmlKey);
+       myKeyLen = CFDataGetLength(xmlKey);
+
+       /* send the key & data to the server */
+       status = notifyadd(sessionPrivate->server,
+                          myKeyRef,
+                          myKeyLen,
+                          regexOptions,
+                          (int *)&scd_status);
+
+       /* clean up */
+       CFRelease(xmlKey);
+
+       if (status != KERN_SUCCESS) {
+               if (status != MACH_SEND_INVALID_DEST)
+                       SCDLog(LOG_DEBUG, CFSTR("notifyadd(): %s"), mach_error_string(status));
+               (void) mach_port_destroy(mach_task_self(), sessionPrivate->server);
+               sessionPrivate->server = MACH_PORT_NULL;
+               return SCD_NOSERVER;
+       }
+
+       return scd_status;
+}
diff --git a/SystemConfiguration.fproj/SCDNotifierCancel.c b/SystemConfiguration.fproj/SCDNotifierCancel.c
new file mode 100644 (file)
index 0000000..3ee6d14
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include <mach/mach.h>
+#include <mach/mach_error.h>
+
+#include <SystemConfiguration/SCD.h>
+#include "config.h"            /* MiG generated file */
+#include "SCDPrivate.h"
+
+
+SCDStatus
+SCDNotifierCancel(SCDSessionRef session)
+{
+       SCDSessionPrivateRef    sessionPrivate = (SCDSessionPrivateRef)session;
+       kern_return_t           status;
+       SCDStatus               scd_status;
+
+       SCDLog(LOG_DEBUG, CFSTR("SCDNotifierCancel:"));
+
+       if (session == NULL) {
+               return SCD_NOSESSION;           /* you can't do anything without a session */
+       }
+
+       if (sessionPrivate->notifyStatus == NotifierNotRegistered) {
+               /* nothing to do, no notifications have been registered */
+               return SCD_OK;
+       }
+
+       /*  if SCDNotifierInformViaCallback() active, stop the background thread  */
+       if (sessionPrivate->callbackFunction != NULL) {
+
+               if (SCDOptionGet(session, kSCDOptionUseCFRunLoop)) {
+                       SCDLog(LOG_DEBUG, CFSTR("  cancel callback runloop source"));
+
+                       /* XXX invalidating the port is not sufficient, remove the run loop source */
+                       CFRunLoopRemoveSource(CFRunLoopGetCurrent(),
+                                             sessionPrivate->callbackRunLoopSource,
+                                             kCFRunLoopDefaultMode);
+                       CFRelease(sessionPrivate->callbackRunLoopSource);
+
+                       /* invalidate port */
+                       CFMachPortInvalidate(sessionPrivate->callbackPort);
+                       CFRelease(sessionPrivate->callbackPort);
+               } else {
+                       int             ts;
+
+                       SCDLog(LOG_DEBUG, CFSTR("  cancel callback thread"));
+                       ts = pthread_cancel(sessionPrivate->callbackHelper);
+                       if (ts != 0) {
+                               SCDLog(LOG_DEBUG, CFSTR("  pthread_cancel(): %s"), strerror(ts));
+                       }
+               }
+
+               sessionPrivate->callbackFunction        = NULL;
+               sessionPrivate->callbackArgument        = NULL;
+               sessionPrivate->callbackPort            = NULL;
+               sessionPrivate->callbackRunLoopSource   = NULL; /* XXX */
+               sessionPrivate->callbackHelper          = NULL;
+       }
+
+       if (sessionPrivate->server == MACH_PORT_NULL) {
+               return SCD_NOSESSION;           /* you must have an open session to play */
+       }
+
+       status = notifycancel(sessionPrivate->server, (int *)&scd_status);
+
+       /* set notifier inactive */
+       sessionPrivate->notifyStatus = NotifierNotRegistered;
+
+       if (status != KERN_SUCCESS) {
+               if (status != MACH_SEND_INVALID_DEST)
+                       SCDLog(LOG_DEBUG, CFSTR("notifycancel(): %s"), mach_error_string(status));
+               (void) mach_port_destroy(mach_task_self(), sessionPrivate->server);
+               sessionPrivate->server = MACH_PORT_NULL;
+               return SCD_NOSERVER;
+       }
+
+       return scd_status;
+}
diff --git a/SystemConfiguration.fproj/SCDNotifierGetChanges.c b/SystemConfiguration.fproj/SCDNotifierGetChanges.c
new file mode 100644 (file)
index 0000000..1fbf028
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include <mach/mach.h>
+#include <mach/mach_error.h>
+
+#include <SystemConfiguration/SCD.h>
+#include "config.h"            /* MiG generated file */
+#include "SCDPrivate.h"
+
+
+static CFComparisonResult
+sort_keys(const void *p1, const void *p2, void *context) {
+       CFStringRef key1 = (CFStringRef)p1;
+       CFStringRef key2 = (CFStringRef)p2;
+       return CFStringCompare(key1, key2, 0);
+}
+
+
+SCDStatus
+SCDNotifierGetChanges(SCDSessionRef session, CFArrayRef *notifierKeys)
+{
+       SCDSessionPrivateRef    sessionPrivate = (SCDSessionPrivateRef)session;
+       kern_return_t           status;
+       CFDataRef               xmlData;        /* data (XML serialized) */
+       xmlDataOut_t            xmlDataRef;     /* serialized data */
+       int                     xmlDataLen;
+       SCDStatus               scd_status;
+       CFArrayRef              allKeys;
+       CFStringRef             xmlError;
+       CFIndex                 keyCnt;
+       CFMutableArrayRef       sortedKeys;
+
+       SCDLog(LOG_DEBUG, CFSTR("SCDNotifierGetChanges:"));
+
+       if ((session == NULL) || (sessionPrivate->server == MACH_PORT_NULL)) {
+               return SCD_NOSESSION;
+       }
+
+       /* send the key & fetch the associated data from the server */
+       status = notifychanges(sessionPrivate->server,
+                              &xmlDataRef,
+                              &xmlDataLen,
+                              (int *)&scd_status);
+
+       if (status != KERN_SUCCESS) {
+               if (status != MACH_SEND_INVALID_DEST)
+                       SCDLog(LOG_DEBUG, CFSTR("notifychanges(): %s"), mach_error_string(status));
+               (void) mach_port_destroy(mach_task_self(), sessionPrivate->server);
+               sessionPrivate->server = MACH_PORT_NULL;
+               return SCD_NOSERVER;
+       }
+
+       if (scd_status != SCD_OK) {
+               status = vm_deallocate(mach_task_self(), (vm_address_t)xmlDataRef, xmlDataLen);
+               if (status != KERN_SUCCESS) {
+                       SCDLog(LOG_DEBUG, CFSTR("vm_deallocate(): %s"), mach_error_string(status));
+                       /* non-fatal???, proceed */
+               }
+               *notifierKeys = NULL;
+               return scd_status;
+       }
+
+       /* un-serialize the list of keys which have changed */
+       xmlData = CFDataCreate(NULL, xmlDataRef, xmlDataLen);
+       status = vm_deallocate(mach_task_self(), (vm_address_t)xmlDataRef, xmlDataLen);
+       if (status != KERN_SUCCESS) {
+               SCDLog(LOG_DEBUG, CFSTR("vm_deallocate(): %s"), mach_error_string(status));
+               /* non-fatal???, proceed */
+       }
+       allKeys = CFPropertyListCreateFromXMLData(NULL,
+                                                 xmlData,
+                                                 kCFPropertyListImmutable,
+                                                 &xmlError);
+       CFRelease(xmlData);
+       if (xmlError) {
+               SCDLog(LOG_DEBUG, CFSTR("CFPropertyListCreateFromXMLData() list: %s"), xmlError);
+               return SCD_FAILED;
+       }
+
+       keyCnt = CFArrayGetCount(allKeys);
+       sortedKeys = CFArrayCreateMutableCopy(NULL, keyCnt, allKeys);
+       CFRelease(allKeys);
+       CFArraySortValues(sortedKeys,
+                         CFRangeMake(0, keyCnt),
+                         sort_keys,
+                         NULL);
+
+       *notifierKeys = sortedKeys;
+       return scd_status;
+}
diff --git a/SystemConfiguration.fproj/SCDNotifierInformViaCallback.c b/SystemConfiguration.fproj/SCDNotifierInformViaCallback.c
new file mode 100644 (file)
index 0000000..481d5b1
--- /dev/null
@@ -0,0 +1,275 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include <mach/mach.h>
+#include <mach/mach_error.h>
+
+#include <SystemConfiguration/SCD.h>
+#include "config.h"            /* MiG generated file */
+#include "SCDPrivate.h"
+
+
+static void
+informCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
+{
+       SCDSessionRef           session        = (SCDSessionRef)info;
+       SCDSessionPrivateRef    sessionPrivate = (SCDSessionPrivateRef)session;
+       mach_msg_empty_rcv_t    *buf           = msg;
+       mach_msg_id_t           msgid          = buf->header.msgh_id;
+       SCDCallbackRoutine_t    cbFunc         = sessionPrivate->callbackFunction;
+       void                    *cbArg         = sessionPrivate->callbackArgument;
+
+       if (msgid == MACH_NOTIFY_NO_SENDERS) {
+               /* the server died, disable additional callbacks */
+               SCDLog(LOG_DEBUG, CFSTR("  notifier port closed, disabling notifier"));
+       } else if (cbFunc == NULL) {
+               /* there is no (longer) a callback function, disable additional callbacks */
+               SCDLog(LOG_DEBUG, CFSTR("  no callback function, disabling notifier"));
+       } else {
+               SCDLog(LOG_DEBUG, CFSTR("  executing notifiction function"));
+               if ((*cbFunc)(session, cbArg)) {
+                       /*
+                        * callback function returned success.
+                        */
+                       return;
+               } else {
+                       SCDLog(LOG_DEBUG, CFSTR("  callback returned error, disabling notifier"));
+               }
+       }
+
+#ifdef DEBUG
+       if (port != sessionPrivate->callbackPort) {
+               SCDLog(LOG_DEBUG, CFSTR("informCallback, why is port != callbackPort?"));
+       }
+#endif /* DEBUG */
+
+       /* we have encountered some type of error, disable additional callbacks */
+
+       /* XXX invalidating the port is not sufficient, remove the run loop source */
+       CFRunLoopRemoveSource(CFRunLoopGetCurrent(),
+                             sessionPrivate->callbackRunLoopSource,
+                             kCFRunLoopDefaultMode);
+       CFRelease(sessionPrivate->callbackRunLoopSource);
+
+       /* invalidate port */
+       CFMachPortInvalidate(port);
+       CFRelease(port);
+
+       sessionPrivate->notifyStatus            = NotifierNotRegistered;
+       sessionPrivate->callbackFunction        = NULL;
+       sessionPrivate->callbackArgument        = NULL;
+       sessionPrivate->callbackPort            = NULL;
+       sessionPrivate->callbackRunLoopSource   = NULL; /* XXX */
+
+       return;
+}
+
+
+static void
+cleanupMachPort(void *ptr)
+{
+       mach_port_t     *port = (mach_port_t *)ptr;
+
+       SCDLog(LOG_DEBUG, CFSTR("  cleaning up notification port %d"), *port);
+       if (*port != MACH_PORT_NULL) {
+               (void) mach_port_destroy(mach_task_self(), *port);
+               free(port);
+       }
+
+       return;
+}
+
+
+static void *
+watcherThread(void *arg)
+{
+       SCDSessionRef           session        = (SCDSessionRef)arg;
+       SCDSessionPrivateRef    sessionPrivate = (SCDSessionPrivateRef)session;
+       SCDCallbackRoutine_t    cbFunc         = sessionPrivate->callbackFunction;
+       void                    *cbArg         = sessionPrivate->callbackArgument;
+       mach_port_t             *port          = malloc(sizeof(mach_port_t));
+
+       *port = CFMachPortGetPort(sessionPrivate->callbackPort);
+       pthread_cleanup_push(cleanupMachPort, (void *)port);
+
+       while (TRUE) {
+               mach_msg_id_t   msgid;
+
+               SCDLog(LOG_DEBUG, CFSTR("Callback thread waiting, port=%d, tid=0x%08x"),
+                      *port, pthread_self());
+
+               msgid = _waitForMachMessage(*port);
+
+               if (msgid == MACH_NOTIFY_NO_SENDERS) {
+                       /* the server closed the notifier port, disable additional callbacks */
+                       SCDLog(LOG_DEBUG, CFSTR("  notifier port closed, disabling notifier"));
+                       break;
+               }
+
+               if (msgid == -1) {
+                       mach_port_type_t        pt;
+
+                       /* an error was detected, disable additional callbacks */
+                       SCDLog(LOG_DEBUG, CFSTR("  server failure, disabling notifier"));
+
+                       /* check if the server connection is not valid, close if necessary */
+                       if ((mach_port_type(mach_task_self(), sessionPrivate->server, &pt) == KERN_SUCCESS) &&
+                           (pt & MACH_PORT_TYPE_DEAD_NAME)) {
+                               SCDLog(LOG_DEBUG, CFSTR("  server process died, destroying (dead) port"));
+                               (void) mach_port_destroy(mach_task_self(), sessionPrivate->server);
+                               sessionPrivate->server = MACH_PORT_NULL;
+                       }
+                       break;
+               }
+
+               if (cbFunc == NULL) {
+                       /* there is no (longer) a callback function, disable additional callbacks */
+                       SCDLog(LOG_DEBUG, CFSTR("  no callback function, disabling notifier"));
+                       break;
+               }
+
+               SCDLog(LOG_DEBUG, CFSTR("  executing notifiction function"));
+
+               if (!(*cbFunc)(session, cbArg)) {
+                       /*
+                        * callback function returned an error, exit the thread
+                        */
+                       break;
+               }
+
+       }
+
+       /*
+        * pop the cleanup routine for the "port" mach port. We end up calling
+        * mach_port_destroy() in the process.
+        */
+       pthread_cleanup_pop(1);
+
+       pthread_exit (NULL);
+       return NULL;
+}
+
+
+SCDStatus
+SCDNotifierInformViaCallback(SCDSessionRef session, SCDCallbackRoutine_t func, void *arg)
+{
+       SCDSessionPrivateRef    sessionPrivate = (SCDSessionPrivateRef)session;
+       kern_return_t           status;
+       mach_port_t             port;
+       mach_port_t             oldNotify;
+       SCDStatus               scd_status;
+       CFMachPortContext       context = { 0, (void *)session, NULL, NULL, NULL };
+
+       SCDLog(LOG_DEBUG, CFSTR("SCDNotifierInformViaCallback:"));
+
+       if ((session == NULL) || (sessionPrivate->server == MACH_PORT_NULL)) {
+               return SCD_NOSESSION;   /* you must have an open session to play */
+       }
+
+       if (sessionPrivate->notifyStatus != NotifierNotRegistered) {
+               /* sorry, you can only have one notification registered at once */
+               return SCD_NOTIFIERACTIVE;
+       }
+
+       if (func == NULL) {
+               /* sorry, you must specify a callback function */
+               return SCD_INVALIDARGUMENT;
+       }
+
+       /* Allocating port (for server response) */
+       sessionPrivate->callbackPort = CFMachPortCreate(NULL,
+                                                       informCallback,
+                                                       &context,
+                                                       NULL);
+
+       /* Request a notification when/if the server dies */
+       port = CFMachPortGetPort(sessionPrivate->callbackPort);
+       status = mach_port_request_notification(mach_task_self(),
+                                               port,
+                                               MACH_NOTIFY_NO_SENDERS,
+                                               1,
+                                               port,
+                                               MACH_MSG_TYPE_MAKE_SEND_ONCE,
+                                               &oldNotify);
+       if (status != KERN_SUCCESS) {
+               SCDLog(LOG_DEBUG, CFSTR("mach_port_request_notification(): %s"), mach_error_string(status));
+               CFMachPortInvalidate(sessionPrivate->callbackPort);
+               CFRelease(sessionPrivate->callbackPort);
+               return SCD_FAILED;
+       }
+
+#ifdef DEBUG
+       if (oldNotify != MACH_PORT_NULL) {
+               SCDLog(LOG_DEBUG, CFSTR("SCDNotifierInformViaCallback(): why is oldNotify != MACH_PORT_NULL?"));
+       }
+#endif /* DEBUG */
+
+       /* Requesting notification via mach port */
+       status = notifyviaport(sessionPrivate->server,
+                              port,
+                              0,
+                              (int *)&scd_status);
+
+       if (status != KERN_SUCCESS) {
+               if (status != MACH_SEND_INVALID_DEST)
+                       SCDLog(LOG_DEBUG, CFSTR("notifyviaport(): %s"), mach_error_string(status));
+               CFMachPortInvalidate(sessionPrivate->callbackPort);
+               CFRelease(sessionPrivate->callbackPort);
+               (void) mach_port_destroy(mach_task_self(), sessionPrivate->server);
+               sessionPrivate->server = MACH_PORT_NULL;
+               return SCD_NOSERVER;
+       }
+
+       if (scd_status != SCD_OK) {
+               return scd_status;
+       }
+
+       /* set notifier active */
+       sessionPrivate->notifyStatus     = Using_NotifierInformViaCallback;
+       sessionPrivate->callbackFunction = func;
+       sessionPrivate->callbackArgument = arg;
+
+       if (SCDOptionGet(session, kSCDOptionUseCFRunLoop)) {
+               /* Creating/adding a run loop source for the port */
+               sessionPrivate->callbackRunLoopSource =
+                       CFMachPortCreateRunLoopSource(NULL, sessionPrivate->callbackPort, 0);
+               CFRunLoopAddSource(CFRunLoopGetCurrent(),
+                                  sessionPrivate->callbackRunLoopSource,
+                                  kCFRunLoopDefaultMode);
+       } else {
+               pthread_attr_t          tattr;
+
+               SCDLog(LOG_DEBUG, CFSTR("Starting background thread to watch for notifications..."));
+               pthread_attr_init(&tattr);
+               pthread_attr_setscope(&tattr, PTHREAD_SCOPE_SYSTEM);
+               pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
+               pthread_attr_setstacksize(&tattr, 96 * 1024); // each thread gets a 96K stack
+               pthread_create(&sessionPrivate->callbackHelper,
+                              &tattr,
+                              watcherThread,
+                              (void *)session);
+               pthread_attr_destroy(&tattr);
+               SCDLog(LOG_DEBUG, CFSTR("  thread id=0x%08x"), sessionPrivate->callbackHelper);
+       }
+
+       return SCD_OK;
+}
diff --git a/SystemConfiguration.fproj/SCDNotifierInformViaFD.c b/SystemConfiguration.fproj/SCDNotifierInformViaFD.c
new file mode 100644 (file)
index 0000000..48ba04f
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include <paths.h>
+#include <unistd.h>
+#include <mach/mach.h>
+#include <mach/mach_error.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <SystemConfiguration/SCD.h>
+#include "config.h"            /* MiG generated file */
+#include "SCDPrivate.h"
+
+
+SCDStatus
+SCDNotifierInformViaFD(SCDSessionRef   session,
+                      int32_t          identifier,
+                      int              *fd)
+{
+       SCDSessionPrivateRef    sessionPrivate = (SCDSessionPrivateRef)session;
+       kern_return_t           status;
+       SCDStatus               scd_status;
+       struct sockaddr_un      un;
+       int                     sock;
+
+       SCDLog(LOG_DEBUG, CFSTR("SCDNotifierInformViaFD:"));
+
+       if ((session == NULL) || (sessionPrivate->server == MACH_PORT_NULL)) {
+               return SCD_NOSESSION;   /* you must have an open session to play */
+       }
+
+       if (sessionPrivate->notifyStatus != NotifierNotRegistered) {
+               /* sorry, you can only have one notification registered at once */
+               return SCD_NOTIFIERACTIVE;
+       }
+
+       if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
+               SCDLog(LOG_NOTICE, CFSTR("socket: %s"), strerror(errno));
+               return SCD_FAILED;
+       }
+
+       /* establish a UNIX domain socket for server->client notification */
+       bzero(&un, sizeof(un));
+       un.sun_family = AF_UNIX;
+       snprintf(un.sun_path,
+                sizeof(un.sun_path)-1,
+                "%s%s-%d",
+                _PATH_VARTMP,
+                "SCDNotifierInformViaFD",
+                sessionPrivate->server);
+
+       if (bind(sock, (struct sockaddr *)&un, sizeof(un)) == -1) {
+               SCDLog(LOG_NOTICE, CFSTR("bind: %s"), strerror(errno));
+               (void) close(sock);
+               return SCD_FAILED;
+       }
+
+       if (listen(sock, 0) == -1) {
+               SCDLog(LOG_NOTICE, CFSTR("listen: %s"), strerror(errno));
+               (void) close(sock);
+               return SCD_FAILED;
+       }
+
+       status = notifyviafd(sessionPrivate->server,
+                            un.sun_path,
+                            strlen(un.sun_path),
+                            identifier,
+                            (int *)&scd_status);
+
+       if (status != KERN_SUCCESS) {
+               if (status != MACH_SEND_INVALID_DEST)
+                       SCDLog(LOG_DEBUG, CFSTR("notifyviafd(): %s"), mach_error_string(status));
+               (void) mach_port_destroy(mach_task_self(), sessionPrivate->server);
+               sessionPrivate->server = MACH_PORT_NULL;
+               return SCD_NOSERVER;
+       }
+
+       *fd = accept(sock, 0, 0);
+       if (*fd == -1) {
+               SCDLog(LOG_NOTICE, CFSTR("accept: %s"), strerror(errno));
+               (void) close(sock);
+               return SCD_FAILED;
+       }
+       (void) close(sock);
+
+       /* set notifier active */
+       sessionPrivate->notifyStatus = Using_NotifierInformViaFD;
+
+       return scd_status;
+}
diff --git a/SystemConfiguration.fproj/SCDNotifierInformViaMachPort.c b/SystemConfiguration.fproj/SCDNotifierInformViaMachPort.c
new file mode 100644 (file)
index 0000000..848579f
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include <mach/mach.h>
+#include <mach/mach_error.h>
+
+#include <SystemConfiguration/SCD.h>
+#include "config.h"            /* MiG generated file */
+#include "SCDPrivate.h"
+
+
+SCDStatus
+SCDNotifierInformViaMachPort(SCDSessionRef session, mach_msg_id_t identifier, mach_port_t *port)
+{
+       SCDSessionPrivateRef    sessionPrivate = (SCDSessionPrivateRef)session;
+       kern_return_t           status;
+       mach_port_t             oldNotify;
+       SCDStatus               scd_status;
+
+       SCDLog(LOG_DEBUG, CFSTR("SCDNotifierInformViaMachPort:"));
+
+       if ((session == NULL) || (sessionPrivate->server == MACH_PORT_NULL)) {
+               return SCD_NOSESSION;   /* you must have an open session to play */
+       }
+
+       if (sessionPrivate->notifyStatus != NotifierNotRegistered) {
+               /* sorry, you can only have one notification registered at once */
+               return SCD_NOTIFIERACTIVE;
+       }
+
+       SCDLog(LOG_DEBUG, CFSTR("Allocating port (for server response)"));
+       status = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, port);
+       if (status != KERN_SUCCESS) {
+               SCDLog(LOG_DEBUG, CFSTR("mach_port_allocate(): %s"), mach_error_string(status));
+               return SCD_FAILED;
+       }
+       SCDLog(LOG_DEBUG, CFSTR("  port = %d"), *port);
+
+       status = mach_port_insert_right(mach_task_self(),
+                                       *port,
+                                       *port,
+                                       MACH_MSG_TYPE_MAKE_SEND);
+       if (status != KERN_SUCCESS) {
+               SCDLog(LOG_DEBUG, CFSTR("mach_port_insert_right(): %s"), mach_error_string(status));
+               (void) mach_port_destroy(mach_task_self(), *port);
+               *port = MACH_PORT_NULL;
+               return SCD_FAILED;
+       }
+
+       /* Request a notification when/if the server dies */
+       status = mach_port_request_notification(mach_task_self(),
+                                               *port,
+                                               MACH_NOTIFY_NO_SENDERS,
+                                               1,
+                                               *port,
+                                               MACH_MSG_TYPE_MAKE_SEND_ONCE,
+                                               &oldNotify);
+       if (status != KERN_SUCCESS) {
+               SCDLog(LOG_DEBUG, CFSTR("mach_port_request_notification(): %s"), mach_error_string(status));
+               (void) mach_port_destroy(mach_task_self(), *port);
+               *port = MACH_PORT_NULL;
+               return SCD_FAILED;
+       }
+
+#ifdef DEBUG
+       if (oldNotify != MACH_PORT_NULL) {
+               SCDLog(LOG_DEBUG, CFSTR("SCDNotifierInformViaMachPort(): why is oldNotify != MACH_PORT_NULL?"));
+       }
+#endif /* DEBUG */
+
+       status = notifyviaport(sessionPrivate->server,
+                              *port,
+                              identifier,
+                              (int *)&scd_status);
+
+       if (status != KERN_SUCCESS) {
+               if (status != MACH_SEND_INVALID_DEST)
+                       SCDLog(LOG_DEBUG, CFSTR("notifyviaport(): %s"), mach_error_string(status));
+               (void) mach_port_destroy(mach_task_self(), *port);
+               *port = MACH_PORT_NULL;
+               (void) mach_port_destroy(mach_task_self(), sessionPrivate->server);
+               sessionPrivate->server = MACH_PORT_NULL;
+               return SCD_NOSERVER;
+       }
+
+       /* set notifier active */
+       sessionPrivate->notifyStatus = Using_NotifierInformViaMachPort;
+
+       return scd_status;
+}
diff --git a/SystemConfiguration.fproj/SCDNotifierInformViaSignal.c b/SystemConfiguration.fproj/SCDNotifierInformViaSignal.c
new file mode 100644 (file)
index 0000000..01e0c14
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include <mach/mach.h>
+#include <mach/mach_error.h>
+
+#include <SystemConfiguration/SCD.h>
+#include "config.h"            /* MiG generated file */
+#include "SCDPrivate.h"
+
+
+SCDStatus
+SCDNotifierInformViaSignal(SCDSessionRef session, pid_t pid, int sig)
+{
+       SCDSessionPrivateRef    sessionPrivate = (SCDSessionPrivateRef)session;
+
+       kern_return_t   status;
+       SCDStatus       scd_status;
+       task_t          task;
+
+       SCDLog(LOG_DEBUG, CFSTR("SCDNotifierInformViaSignal:"));
+       SCDLog(LOG_DEBUG, CFSTR("  pid = %d"), pid);
+       SCDLog(LOG_DEBUG, CFSTR("  sig = %d"), sig);
+
+       if ((session == NULL) || (sessionPrivate->server == MACH_PORT_NULL)) {
+               return SCD_NOSESSION;   /* you must have an open session to play */
+       }
+
+       if (SCDOptionGet(NULL, kSCDOptionIsServer)) {
+               /* sorry, neither the server nor any plug-ins can "wait" */
+               return SCD_FAILED;
+       }
+
+       if (sessionPrivate->notifyStatus != NotifierNotRegistered) {
+               /* sorry, you can only have one notification registered at once */
+               return SCD_NOTIFIERACTIVE;
+       }
+
+       if ((sig <= 0) || (sig > NSIG)) {
+               /* sorry, you must specify a valid signal */
+               return SCD_INVALIDARGUMENT;
+       }
+
+       status = task_for_pid(mach_task_self(), pid, &task);
+       if (status != KERN_SUCCESS) {
+               SCDLog(LOG_DEBUG, CFSTR("task_for_pid(): %s"), mach_error_string(status));
+               return SCD_FAILED;
+       }
+
+       status = notifyviasignal(sessionPrivate->server, task, sig, (int *)&scd_status);
+
+       if (status != KERN_SUCCESS) {
+               if (status != MACH_SEND_INVALID_DEST)
+                       SCDLog(LOG_DEBUG, CFSTR("notifyviasignal(): %s"), mach_error_string(status));
+               (void) mach_port_destroy(mach_task_self(), sessionPrivate->server);
+               sessionPrivate->server = MACH_PORT_NULL;
+               return SCD_NOSERVER;
+       }
+
+       /* set notifier active */
+       sessionPrivate->notifyStatus = Using_NotifierInformViaSignal;
+
+       return scd_status;
+}
diff --git a/SystemConfiguration.fproj/SCDNotifierList.c b/SystemConfiguration.fproj/SCDNotifierList.c
new file mode 100644 (file)
index 0000000..e821232
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include <mach/mach.h>
+#include <mach/mach_error.h>
+
+#include <SystemConfiguration/SCD.h>
+#include "config.h"            /* MiG generated file */
+#include "SCDPrivate.h"
+
+static CFComparisonResult
+sort_keys(const void *p1, const void *p2, void *context) {
+       CFStringRef key1 = (CFStringRef)p1;
+       CFStringRef key2 = (CFStringRef)p2;
+       return CFStringCompare(key1, key2, 0);
+}
+
+
+SCDStatus
+SCDNotifierList(SCDSessionRef session, int regexOptions, CFArrayRef *notifierKeys)
+{
+       SCDSessionPrivateRef    sessionPrivate = (SCDSessionPrivateRef)session;
+       CFIndex                 keyCnt;
+       void                    **keyRefs;
+       CFArrayRef              keys;
+       CFMutableArrayRef       sortedKeys;
+
+       SCDLog(LOG_DEBUG, CFSTR("SCDNotifierList:"));
+
+       if ((session == NULL) || (sessionPrivate->server == MACH_PORT_NULL)) {
+               return SCD_NOSESSION;
+       }
+
+       if (regexOptions & kSCDRegexKey) {
+               keyCnt  = CFSetGetCount(sessionPrivate->reKeys);
+               keyRefs = CFAllocatorAllocate(NULL, keyCnt * sizeof(CFStringRef), 0);
+               CFSetGetValues(sessionPrivate->reKeys, keyRefs);
+               keys = CFArrayCreate(NULL, keyRefs, keyCnt, &kCFTypeArrayCallBacks);
+               CFAllocatorDeallocate(NULL, keyRefs);
+       } else {
+               keyCnt  = CFSetGetCount(sessionPrivate->keys);
+               keyRefs = CFAllocatorAllocate(NULL, keyCnt * sizeof(CFStringRef), 0);
+               CFSetGetValues(sessionPrivate->keys, keyRefs);
+               keys = CFArrayCreate(NULL, keyRefs, keyCnt, &kCFTypeArrayCallBacks);
+               CFAllocatorDeallocate(NULL, keyRefs);
+       }
+
+       sortedKeys = CFArrayCreateMutableCopy(NULL, keyCnt, keys);
+       CFRelease(keys);
+       CFArraySortValues(sortedKeys, CFRangeMake(0, keyCnt), sort_keys, NULL);
+
+       *notifierKeys = sortedKeys;
+       return SCD_OK;
+}
diff --git a/SystemConfiguration.fproj/SCDNotifierRemove.c b/SystemConfiguration.fproj/SCDNotifierRemove.c
new file mode 100644 (file)
index 0000000..e062600
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include <sys/types.h>
+#include <regex.h>
+
+#include <mach/mach.h>
+#include <mach/mach_error.h>
+
+#include <SystemConfiguration/SCD.h>
+#include "config.h"            /* MiG generated file */
+#include "SCDPrivate.h"
+
+
+SCDStatus
+SCDNotifierRemove(SCDSessionRef session, CFStringRef key, int regexOptions)
+{
+       SCDSessionPrivateRef    sessionPrivate = (SCDSessionPrivateRef)session;
+       kern_return_t           status;
+       CFDataRef               xmlKey;         /* serialized key */
+       xmlData_t               myKeyRef;
+       CFIndex                 myKeyLen;
+       SCDStatus               scd_status;
+
+       SCDLog(LOG_DEBUG, CFSTR("SCDNotifierRemove:"));
+       SCDLog(LOG_DEBUG, CFSTR("  key          = %@"), key);
+       SCDLog(LOG_DEBUG, CFSTR("  regexOptions = %0o"), regexOptions);
+
+       if (key == NULL) {
+               return SCD_INVALIDARGUMENT;     /* no key specified */
+       }
+
+       if (session == NULL) {
+               return SCD_NOSESSION;           /* you can't do anything without a session */
+       }
+
+       /*
+        * remove key from this sessions notifier list after checking that
+        * it was previously defined.
+        */
+       if (regexOptions & kSCDRegexKey) {
+               if (!CFSetContainsValue(sessionPrivate->reKeys, key))
+                       return SCD_NOKEY;               /* sorry, key does not exist in notifier list */
+               CFSetRemoveValue(sessionPrivate->reKeys, key);  /* remove key from this sessions notifier list */
+       } else {
+               if (!CFSetContainsValue(sessionPrivate->keys, key))
+                       return SCD_NOKEY;               /* sorry, key does not exist in notifier list */
+               CFSetRemoveValue(sessionPrivate->keys, key);    /* remove key from this sessions notifier list */
+       }
+
+       if (sessionPrivate->server == MACH_PORT_NULL) {
+               return SCD_NOSESSION;           /* you can't do anything with a closed session */
+       }
+
+       /* serialize the key */
+       xmlKey = CFPropertyListCreateXMLData(NULL, key);
+       myKeyRef = (xmlData_t)CFDataGetBytePtr(xmlKey);
+       myKeyLen = CFDataGetLength(xmlKey);
+
+       /* send the key to the server */
+       status = notifyremove(sessionPrivate->server,
+                             myKeyRef,
+                             myKeyLen,
+                             regexOptions,
+                             (int *)&scd_status);
+
+       /* clean up */
+       CFRelease(xmlKey);
+
+       if (status != KERN_SUCCESS) {
+               if (status != MACH_SEND_INVALID_DEST)
+                       SCDLog(LOG_DEBUG, CFSTR("notifyremove(): %s"), mach_error_string(status));
+               (void) mach_port_destroy(mach_task_self(), sessionPrivate->server);
+               sessionPrivate->server = MACH_PORT_NULL;
+               return SCD_NOSERVER;
+       }
+
+       return scd_status;
+}
diff --git a/SystemConfiguration.fproj/SCDNotifierWait.c b/SystemConfiguration.fproj/SCDNotifierWait.c
new file mode 100644 (file)
index 0000000..67cf1d9
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include <mach/mach.h>
+#include <mach/mach_error.h>
+
+#include <SystemConfiguration/SCD.h>
+#include "config.h"            /* MiG generated file */
+#include "SCDPrivate.h"
+
+
+SCDStatus
+SCDNotifierWait(SCDSessionRef session)
+{
+       SCDSessionPrivateRef    sessionPrivate = (SCDSessionPrivateRef)session;
+       kern_return_t           status;
+       mach_port_t             port;
+       mach_port_t             oldNotify;
+       SCDStatus               scd_status;
+       mach_msg_id_t           msgid;
+
+       SCDLog(LOG_DEBUG, CFSTR("SCDNotifierWait:"));
+
+       if ((session == NULL) || (sessionPrivate->server == MACH_PORT_NULL)) {
+               return SCD_NOSESSION;   /* you must have an open session to play */
+       }
+
+       if (SCDOptionGet(NULL, kSCDOptionIsServer)) {
+               /* sorry, neither the server nor any plug-ins can "wait" */
+               return SCD_FAILED;
+       }
+
+       if (sessionPrivate->notifyStatus != NotifierNotRegistered) {
+               /* sorry, you can only have one notification registered at once */
+               return SCD_NOTIFIERACTIVE;
+       }
+
+       SCDLog(LOG_DEBUG, CFSTR("Allocating port (for server response)"));
+       status = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
+       if (status != KERN_SUCCESS) {
+               SCDLog(LOG_DEBUG, CFSTR("mach_port_allocate(): %s"), mach_error_string(status));
+               return SCD_FAILED;
+       }
+       SCDLog(LOG_DEBUG, CFSTR("  port = %d"), port);
+
+       status = mach_port_insert_right(mach_task_self(),
+                                       port,
+                                       port,
+                                       MACH_MSG_TYPE_MAKE_SEND);
+       if (status != KERN_SUCCESS) {
+               SCDLog(LOG_DEBUG, CFSTR("mach_port_insert_right(): %s"), mach_error_string(status));
+               (void) mach_port_destroy(mach_task_self(), port);
+               return SCD_FAILED;
+       }
+
+       /* Request a notification when/if the server dies */
+       status = mach_port_request_notification(mach_task_self(),
+                                               port,
+                                               MACH_NOTIFY_NO_SENDERS,
+                                               1,
+                                               port,
+                                               MACH_MSG_TYPE_MAKE_SEND_ONCE,
+                                               &oldNotify);
+       if (status != KERN_SUCCESS) {
+               SCDLog(LOG_DEBUG, CFSTR("mach_port_request_notification(): %s"), mach_error_string(status));
+               (void) mach_port_destroy(mach_task_self(), port);
+               return SCD_FAILED;
+       }
+
+#ifdef DEBUG
+       if (oldNotify != MACH_PORT_NULL) {
+               SCDLog(LOG_DEBUG, CFSTR("SCDNotifierWait(): why is oldNotify != MACH_PORT_NULL?"));
+       }
+#endif /* DEBUG */
+
+       SCDLog(LOG_DEBUG, CFSTR("Requesting notification via mach port %d"), port);
+       status = notifyviaport(sessionPrivate->server,
+                              port,
+                              0,
+                              (int *)&scd_status);
+
+       if (status != KERN_SUCCESS) {
+               if (status != MACH_SEND_INVALID_DEST)
+                       SCDLog(LOG_DEBUG, CFSTR("notifyviaport(): %s"), mach_error_string(status));
+               (void) mach_port_destroy(mach_task_self(), sessionPrivate->server);
+               sessionPrivate->server = MACH_PORT_NULL;
+               return SCD_NOSERVER;
+       }
+
+       if (scd_status != SCD_OK) {
+               return scd_status;
+       }
+
+       /* set notifier active */
+       sessionPrivate->notifyStatus = Using_NotifierWait;
+
+       SCDLog(LOG_DEBUG, CFSTR("Waiting..."));
+
+       msgid = _waitForMachMessage(port);
+
+       /* set notifier inactive */
+       sessionPrivate->notifyStatus = NotifierNotRegistered;
+
+       if (msgid == MACH_NOTIFY_NO_SENDERS) {
+               /* the server closed the notifier port */
+               SCDLog(LOG_DEBUG, CFSTR("  notifier port closed, destroying port %d"), port);
+               return SCD_NOSERVER;
+       }
+
+       if (msgid == -1) {
+               /* one of the mach routines returned an error */
+               SCDLog(LOG_DEBUG, CFSTR("  communication with server failed, destroying port %d"), port);
+               (void) mach_port_destroy(mach_task_self(), port);
+               return SCD_NOSERVER;
+       }
+
+       SCDLog(LOG_DEBUG, CFSTR("Something changed, cancelling notification request"));
+       status = notifycancel(sessionPrivate->server,
+                             (int *)&scd_status);
+
+       if (status != KERN_SUCCESS) {
+               if (status != MACH_SEND_INVALID_DEST)
+                       SCDLog(LOG_DEBUG, CFSTR("notifycancel(): %s"), mach_error_string(status));
+               (void) mach_port_destroy(mach_task_self(), sessionPrivate->server);
+               sessionPrivate->server = MACH_PORT_NULL;
+               scd_status = SCD_NOSERVER;
+       }
+
+       (void) mach_port_destroy(mach_task_self(), port);
+
+       return scd_status;
+}
diff --git a/SystemConfiguration.fproj/SCDOpen.c b/SystemConfiguration.fproj/SCDOpen.c
new file mode 100644 (file)
index 0000000..7e1f720
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include <mach/mach.h>
+#include <mach/mach_error.h>
+#include <servers/bootstrap.h>
+
+#include <SystemConfiguration/SCD.h>
+#include "config.h"            /* MiG generated file */
+#include "SCDPrivate.h"
+
+
+SCDSessionRef
+_SCDSessionCreatePrivate()
+{
+       SCDSessionRef           newSession;
+       SCDSessionPrivateRef    newPrivate;
+
+       /* allocate space */
+       newSession = (SCDSessionRef)CFAllocatorAllocate(NULL, sizeof(SCDSessionPrivate), 0);
+       newPrivate = (SCDSessionPrivateRef)newSession;
+
+       /* server side of the "configd" session */
+       newPrivate->server         = MACH_PORT_NULL;
+
+       /* per-session flags */
+       SCDOptionSet(newSession, kSCDOptionDebug,        SCDOptionGet(NULL, kSCDOptionDebug       ));
+       SCDOptionSet(newSession, kSCDOptionVerbose,      SCDOptionGet(NULL, kSCDOptionVerbose     ));
+       SCDOptionSet(newSession, kSCDOptionIsLocked,     FALSE);
+       SCDOptionSet(newSession, kSCDOptionUseSyslog,    SCDOptionGet(NULL, kSCDOptionUseSyslog   ));
+       SCDOptionSet(newSession, kSCDOptionUseCFRunLoop, SCDOptionGet(NULL, kSCDOptionUseCFRunLoop));
+
+       /* SCDKeys being watched */
+       newPrivate->keys   = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
+       newPrivate->reKeys = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
+
+       /* No notifications pending */
+       newPrivate->notifyStatus                = NotifierNotRegistered;
+
+       /* "client" information about active (notification) callback */
+       newPrivate->callbackFunction            = NULL;
+       newPrivate->callbackArgument            = NULL;
+       newPrivate->callbackPort                = NULL;
+       newPrivate->callbackRunLoopSource       = NULL; /* XXX */
+       newPrivate->callbackHelper              = NULL;
+
+       /* "server" information associated with SCDNotifierInformViaMachPort(); */
+       newPrivate->notifyPort                  = MACH_PORT_NULL;
+       newPrivate->notifyPortIdentifier        = 0;
+
+       /* "server" information associated with SCDNotifierInformViaFD(); */
+       newPrivate->notifyFile                  = -1;
+       newPrivate->notifyFileIdentifier        = 0;
+
+       /* "server" information associated with SCDNotifierInformViaSignal(); */
+       newPrivate->notifySignal                = 0;
+       newPrivate->notifySignalTask            = TASK_NULL;
+
+       return newSession;
+}
+
+
+SCDStatus
+SCDOpen(SCDSessionRef *session, CFStringRef name)
+{
+       SCDSessionPrivateRef    sessionPrivate;
+       kern_return_t           status;
+       mach_port_t             bootstrap_port;
+       mach_port_t             server;
+       CFDataRef               xmlName;                /* serialized name */
+       xmlData_t               myNameRef;
+       CFIndex                 myNameLen;
+       SCDStatus               scd_status;
+
+       SCDLog(LOG_DEBUG, CFSTR("SCDOpen:"));
+       SCDLog(LOG_DEBUG, CFSTR("  name = %@"), name);
+
+       /*
+        * allocate and initialize a new session
+        */
+       sessionPrivate = (SCDSessionPrivateRef)_SCDSessionCreatePrivate();
+       *session       = (SCDSessionRef)sessionPrivate;
+
+       status = task_get_bootstrap_port(mach_task_self(), &bootstrap_port);
+       if (status != KERN_SUCCESS) {
+               SCDLog(LOG_DEBUG, CFSTR("task_get_bootstrap_port(): %s"), mach_error_string(status));
+               CFAllocatorDeallocate(NULL, sessionPrivate);
+               *session = NULL;
+               return SCD_NOSERVER;
+       }
+
+       status = bootstrap_look_up(bootstrap_port, SCD_SERVER, &server);
+       switch (status) {
+               case BOOTSTRAP_SUCCESS :
+                       /* service currently registered, "a good thing" (tm) */
+                       break;
+               case BOOTSTRAP_UNKNOWN_SERVICE :
+                       /* service not currently registered, try again later */
+                       CFAllocatorDeallocate(NULL, sessionPrivate);
+                       *session = NULL;
+                       return SCD_NOSERVER;
+                       break;
+               default :
+#ifdef DEBUG
+                       SCDLog(LOG_DEBUG, CFSTR("bootstrap_status: %s"), mach_error_string(status));
+#endif /* DEBUG */
+                       CFAllocatorDeallocate(NULL, sessionPrivate);
+                       *session = NULL;
+                       return SCD_NOSERVER;
+       }
+
+       /* serialize the name */
+       xmlName = CFPropertyListCreateXMLData(NULL, name);
+       myNameRef = (xmlData_t)CFDataGetBytePtr(xmlName);
+       myNameLen = CFDataGetLength(xmlName);
+
+       /* open a new session with the server */
+       status = configopen(server, myNameRef, myNameLen, &sessionPrivate->server, (int *)&scd_status);
+
+       /* clean up */
+       CFRelease(xmlName);
+
+       if (status != KERN_SUCCESS) {
+               if (status != MACH_SEND_INVALID_DEST)
+                       SCDLog(LOG_DEBUG, CFSTR("configopen(): %s"), mach_error_string(status));
+               CFAllocatorDeallocate(NULL, sessionPrivate);
+               *session = NULL;
+               return SCD_NOSERVER;
+       }
+
+       SCDLog(LOG_DEBUG, CFSTR("  server port = %d"), sessionPrivate->server);
+       return scd_status;
+}
diff --git a/SystemConfiguration.fproj/SCDPrivate.c b/SystemConfiguration.fproj/SCDPrivate.c
new file mode 100644 (file)
index 0000000..86a6f03
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include <mach/mach.h>
+#include <mach/notify.h>
+#include <mach/mach_error.h>
+#include <pthread.h>
+
+#include "SCDPrivate.h"
+
+
+__private_extern__ mach_msg_id_t
+_waitForMachMessage(mach_port_t port)
+{
+       kern_return_t           status;
+       mach_msg_empty_rcv_t    *buf;
+
+       mach_msg_size_t         size = sizeof(mach_msg_empty_t) + MAX_TRAILER_SIZE;
+
+       status = vm_allocate(mach_task_self(), (vm_address_t *)&buf, size, TRUE);
+       if (status != KERN_SUCCESS) {
+               SCDLog(LOG_DEBUG, CFSTR("vm_allocate(): %s"), mach_error_string(status));
+               return -1;
+       }
+
+       status = mach_msg(&buf->header,                 /* msg */
+                         MACH_RCV_MSG,                 /* options */
+                         0,                            /* send_size */
+                         size,                         /* rcv_size */
+                         port,                         /* rcv_name */
+                         MACH_MSG_TIMEOUT_NONE,        /* timeout */
+                         MACH_PORT_NULL);              /* notify */
+       if (status != KERN_SUCCESS) {
+               SCDLog(LOG_DEBUG, CFSTR("mach_msg(): %s"), mach_error_string(status));
+               return -1;
+       }
+
+       return buf->header.msgh_id;
+}
+
+
+void
+_showMachPortStatus()
+{
+#ifdef DEBUG
+       /* print status of in-use mach ports */
+       if (SCDOptionGet(NULL, kSCDOptionDebug) && SCDOptionGet(NULL, kSCDOptionVerbose)) {
+               kern_return_t           status;
+               mach_port_name_array_t  ports;
+               mach_port_type_array_t  types;
+               int                     pi, pn, tn;
+               CFMutableStringRef      str;
+
+               SCDLog(LOG_DEBUG, CFSTR("----------"));
+
+               /* report on ALL mach ports associated with this task */
+               status = mach_port_names(mach_task_self(), &ports, &pn, &types, &tn);
+               if (status == MACH_MSG_SUCCESS) {
+                       str = CFStringCreateMutable(NULL, 0);
+                       for (pi=0; pi < pn; pi++) {
+                               char    rights[16], *rp = &rights[0];
+
+                               if (types[pi] != MACH_PORT_TYPE_NONE) {
+                                       *rp++ = ' ';
+                                       *rp++ = '(';
+                                       if (types[pi] & MACH_PORT_TYPE_SEND)
+                                               *rp++ = 'S';
+                                       if (types[pi] & MACH_PORT_TYPE_RECEIVE)
+                                               *rp++ = 'R';
+                                       if (types[pi] & MACH_PORT_TYPE_SEND_ONCE)
+                                               *rp++ = 'O';
+                                       if (types[pi] & MACH_PORT_TYPE_PORT_SET)
+                                               *rp++ = 'P';
+                                       if (types[pi] & MACH_PORT_TYPE_DEAD_NAME)
+                                               *rp++ = 'D';
+                                       *rp++ = ')';
+                               }
+                               *rp = '\0';
+                               CFStringAppendFormat(str, NULL, CFSTR(" %d%s"), ports[pi], rights);
+                       }
+                       SCDLog(LOG_DEBUG, CFSTR("Task ports (n=%d):%@"), pn, str);
+                       CFRelease(str);
+               } else {
+                       /* log (but ignore) errors */
+                       SCDLog(LOG_DEBUG, CFSTR("mach_port_names(): %s"), mach_error_string(status));
+               }
+       }
+#endif /* DEBUG */
+       return;
+}
+
+
+void
+_showMachPortReferences(mach_port_t port)
+{
+#ifdef DEBUG
+       kern_return_t           status;
+       mach_port_urefs_t       refs_send       = 0;
+       mach_port_urefs_t       refs_recv       = 0;
+       mach_port_urefs_t       refs_once       = 0;
+       mach_port_urefs_t       refs_pset       = 0;
+       mach_port_urefs_t       refs_dead       = 0;
+
+       SCDLog(LOG_DEBUG, CFSTR("user references for mach port %d"), port);
+
+       status = mach_port_get_refs(mach_task_self(), port, MACH_PORT_RIGHT_SEND,      &refs_send);
+       if (status != KERN_SUCCESS) {
+               SCDLog(LOG_DEBUG, CFSTR("  mach_port_get_refs(MACH_PORT_RIGHT_SEND): %s"), mach_error_string(status));
+               return;
+       }
+
+       status = mach_port_get_refs(mach_task_self(), port, MACH_PORT_RIGHT_RECEIVE,   &refs_recv);
+       if (status != KERN_SUCCESS) {
+               SCDLog(LOG_DEBUG, CFSTR("  mach_port_get_refs(MACH_PORT_RIGHT_RECEIVE): %s"), mach_error_string(status));
+               return;
+       }
+
+       status = mach_port_get_refs(mach_task_self(), port, MACH_PORT_RIGHT_SEND_ONCE, &refs_once);
+       if (status != KERN_SUCCESS) {
+               SCDLog(LOG_DEBUG, CFSTR("  mach_port_get_refs(MACH_PORT_RIGHT_SEND_ONCE): %s"), mach_error_string(status));
+               return;
+       }
+
+       status = mach_port_get_refs(mach_task_self(), port, MACH_PORT_RIGHT_PORT_SET,  &refs_pset);
+       if (status != KERN_SUCCESS) {
+               SCDLog(LOG_DEBUG, CFSTR("  mach_port_get_refs(MACH_PORT_RIGHT_PORT_SET): %s"), mach_error_string(status));
+               return;
+       }
+
+       status = mach_port_get_refs(mach_task_self(), port, MACH_PORT_RIGHT_DEAD_NAME, &refs_dead);
+       if (status != KERN_SUCCESS) {
+               SCDLog(LOG_DEBUG, CFSTR("  mach_port_get_refs(MACH_PORT_RIGHT_DEAD_NAME): %s"), mach_error_string(status));
+               return;
+       }
+
+       SCDLog(LOG_DEBUG,
+              CFSTR("  send = %d, receive = %d, send once = %d, port set = %d, dead name = %d"),
+              refs_send,
+              refs_recv,
+              refs_once,
+              refs_pset,
+              refs_dead);
+
+#endif /* DEBUG */
+       return;
+}
diff --git a/SystemConfiguration.fproj/SCDPrivate.h b/SystemConfiguration.fproj/SCDPrivate.h
new file mode 100644 (file)
index 0000000..3601d35
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#ifndef _SCDPRIVATE_H
+#define _SCDPRIVATE_H
+
+#include <SystemConfiguration/SCD.h>
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <regex.h>
+#include <pthread.h>
+#include <CoreFoundation/CFMachPort.h>
+
+
+/* Define the per-session (and global) flags */
+typedef struct {
+       int                     debug;
+       int                     verbose;
+       boolean_t               isLocked;
+       boolean_t               useSyslog;
+       boolean_t               useCFRunLoop;
+} _SCDFlags;
+
+
+/* Define the status of any registered notification. */
+typedef enum {
+       NotifierNotRegistered = 0,
+       Using_NotifierWait,
+       Using_NotifierInformViaCallback,
+       Using_NotifierInformViaMachPort,
+       Using_NotifierInformViaFD,
+       Using_NotifierInformViaSignal,
+} _SCDNotificationStatus;
+
+
+typedef struct {
+
+       /* server side of the "configd" session */
+       mach_port_t             server;
+
+       /* per-session flags */
+       _SCDFlags               flags;
+
+       /* SCDKeys being watched */
+       CFMutableSetRef         keys;
+       CFMutableSetRef         reKeys;
+
+       /* current status of notification requests */
+       _SCDNotificationStatus  notifyStatus;
+
+       /* "client" information associated with SCDNotifierInformViaCallback() */
+       SCDCallbackRoutine_t    callbackFunction;
+       void                    *callbackArgument;
+       CFMachPortRef           callbackPort;
+       CFRunLoopSourceRef      callbackRunLoopSource;  /* XXX CFMachPortInvalidate() doesn't work */
+       pthread_t               callbackHelper;
+
+       /* "server" information associated with SCDNotifierInformViaMachPort() */
+       mach_port_t             notifyPort;
+       mach_msg_id_t           notifyPortIdentifier;
+
+       /* "server" information associated with SCDNotifierInformViaFD() */
+       int                     notifyFile;
+       int                     notifyFileIdentifier;
+
+       /* "server" information associated with SCDNotifierInformViaSignal() */
+       int                     notifySignal;
+       task_t                  notifySignalTask;
+
+} SCDSessionPrivate, *SCDSessionPrivateRef;
+
+
+typedef struct {
+
+       /* configuration data associated with key */
+       CFPropertyListRef       data;
+
+       /* instance value of last fetched data */
+       int                     instance;
+
+} SCDHandlePrivate, *SCDHandlePrivateRef;
+
+
+/* per-session options */
+typedef enum {
+       kSCDOptionIsLocked = 1024,
+} SCDServerSessionOptions;
+
+
+/* global options */
+typedef enum {
+       kSCDOptionIsServer = 2048,
+} SCDServerGlobalOptions;
+
+
+__BEGIN_DECLS
+
+SCDSessionRef  _SCDSessionCreatePrivate        ();
+
+void           _SCDHandleSetInstance           (SCDHandleRef   handle,
+                                                int            instance);
+
+mach_msg_id_t  _waitForMachMessage             (mach_port_t    port);
+
+void           _showMachPortStatus             ();
+void           _showMachPortReferences         (mach_port_t    port);
+
+__END_DECLS
+
+#endif /* !_SCDPRIVATE_H */
diff --git a/SystemConfiguration.fproj/SCDRemove.c b/SystemConfiguration.fproj/SCDRemove.c
new file mode 100644 (file)
index 0000000..30cc8b1
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include <mach/mach.h>
+#include <mach/mach_error.h>
+
+#include <SystemConfiguration/SCD.h>
+#include "config.h"            /* MiG generated file */
+#include "SCDPrivate.h"
+
+
+SCDStatus
+SCDRemove(SCDSessionRef session, CFStringRef key)
+{
+       SCDSessionPrivateRef    sessionPrivate = (SCDSessionPrivateRef)session;
+       kern_return_t           status;
+       CFDataRef               xmlKey;         /* serialized key */
+       xmlData_t               myKeyRef;
+       CFIndex                 myKeyLen;
+       SCDStatus               scd_status;
+
+       SCDLog(LOG_DEBUG, CFSTR("SCDRemove:"));
+       SCDLog(LOG_DEBUG, CFSTR("  key      = %@"), key);
+
+       if (key == NULL) {
+               return SCD_INVALIDARGUMENT;     /* no key specified */
+       }
+
+       if ((session == NULL) || (sessionPrivate->server == MACH_PORT_NULL)) {
+               return SCD_NOSESSION;           /* you can't do anything with a closed session */
+       }
+
+       /* serialize the key */
+       xmlKey = CFPropertyListCreateXMLData(NULL, key);
+       myKeyRef = (xmlData_t)CFDataGetBytePtr(xmlKey);
+       myKeyLen = CFDataGetLength(xmlKey);
+
+       /* send the key to the server */
+       status = configremove(sessionPrivate->server,
+                             myKeyRef,
+                             myKeyLen,
+                             (int *)&scd_status);
+
+       /* clean up */
+       CFRelease(xmlKey);
+
+       if (status != KERN_SUCCESS) {
+               if (status != MACH_SEND_INVALID_DEST)
+                       SCDLog(LOG_DEBUG, CFSTR("configremove(): %s"), mach_error_string(status));
+               (void) mach_port_destroy(mach_task_self(), sessionPrivate->server);
+               sessionPrivate->server = MACH_PORT_NULL;
+               return SCD_NOSERVER;
+       }
+
+       return scd_status;
+}
diff --git a/SystemConfiguration.fproj/SCDSet.c b/SystemConfiguration.fproj/SCDSet.c
new file mode 100644 (file)
index 0000000..1cb79ad
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include <mach/mach.h>
+#include <mach/mach_error.h>
+
+#include <SystemConfiguration/SCD.h>
+#include "config.h"            /* MiG generated file */
+#include "SCDPrivate.h"
+
+
+SCDStatus
+SCDSet(SCDSessionRef session, CFStringRef key, SCDHandleRef handle)
+{
+       SCDSessionPrivateRef    sessionPrivate = (SCDSessionPrivateRef)session;
+       kern_return_t           status;
+       CFDataRef               xmlKey;         /* serialized key */
+       xmlData_t               myKeyRef;
+       CFIndex                 myKeyLen;
+       CFDataRef               xmlData;        /* serialized data */
+       xmlData_t               myDataRef;
+       CFIndex                 myDataLen;
+       SCDStatus               scd_status;
+       int                     newInstance;
+
+       SCDLog(LOG_DEBUG, CFSTR("SCDSet:"));
+       SCDLog(LOG_DEBUG, CFSTR("  key          = %@"), key);
+       SCDLog(LOG_DEBUG, CFSTR("  data         = %@"), SCDHandleGetData(handle));
+       SCDLog(LOG_DEBUG, CFSTR("  instance     = %d"), SCDHandleGetInstance(handle));
+
+       if (key == NULL) {
+               return SCD_INVALIDARGUMENT;     /* no key specified */
+       }
+
+       if ((session == NULL) || (sessionPrivate->server == MACH_PORT_NULL)) {
+               return SCD_NOSESSION;           /* you can't do anything with a closed session */
+       }
+
+       /* serialize the key and data */
+       xmlKey = CFPropertyListCreateXMLData(NULL, key);
+       myKeyRef = (xmlData_t)CFDataGetBytePtr(xmlKey);
+       myKeyLen = CFDataGetLength(xmlKey);
+
+       xmlData = CFPropertyListCreateXMLData(NULL, SCDHandleGetData(handle));
+       myDataRef = (xmlData_t)CFDataGetBytePtr(xmlData);
+       myDataLen = CFDataGetLength(xmlData);
+
+       /* send the key & data to the server, get new instance id */
+       status = configset(sessionPrivate->server,
+                          myKeyRef,
+                          myKeyLen,
+                          myDataRef,
+                          myDataLen,
+                          SCDHandleGetInstance(handle),
+                          &newInstance,
+                          (int *)&scd_status);
+
+       /* clean up */
+       CFRelease(xmlKey);
+       CFRelease(xmlData);
+
+       if (status != KERN_SUCCESS) {
+               if (status != MACH_SEND_INVALID_DEST)
+                       SCDLog(LOG_DEBUG, CFSTR("configset(): %s"), mach_error_string(status));
+               (void) mach_port_destroy(mach_task_self(), sessionPrivate->server);
+               sessionPrivate->server = MACH_PORT_NULL;
+               return SCD_NOSERVER;
+       }
+
+       if (scd_status == SCD_OK) {
+               _SCDHandleSetInstance(handle, newInstance);
+       }
+
+       SCDLog(LOG_DEBUG, CFSTR("  new instance = %d"), SCDHandleGetInstance(handle));
+
+       return scd_status;
+}
diff --git a/SystemConfiguration.fproj/SCDSnapshot.c b/SystemConfiguration.fproj/SCDSnapshot.c
new file mode 100644 (file)
index 0000000..1339cda
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include <mach/mach.h>
+#include <mach/mach_error.h>
+
+#include <SystemConfiguration/SCD.h>
+#include "config.h"            /* MiG generated file */
+#include "SCDPrivate.h"
+
+
+SCDStatus
+SCDSnapshot(SCDSessionRef session)
+{
+       SCDSessionPrivateRef    sessionPrivate = (SCDSessionPrivateRef)session;
+       kern_return_t           status;
+       SCDStatus               scd_status;
+
+       SCDLog(LOG_DEBUG, CFSTR("SCDSnapshot:"));
+
+       if ((session == NULL) || (sessionPrivate->server == MACH_PORT_NULL)) {
+               return SCD_NOSESSION;   /* you must have an open session to play */
+       }
+
+       status = snapshot(sessionPrivate->server, (int *)&scd_status);
+
+       if (status != KERN_SUCCESS) {
+               if (status != MACH_SEND_INVALID_DEST)
+                       SCDLog(LOG_DEBUG, CFSTR("snapshot(): %s"), mach_error_string(status));
+               (void) mach_port_destroy(mach_task_self(), sessionPrivate->server);
+               sessionPrivate->server = MACH_PORT_NULL;
+               return SCD_NOSERVER;
+       }
+
+       return scd_status;
+}
diff --git a/SystemConfiguration.fproj/SCDTouch.c b/SystemConfiguration.fproj/SCDTouch.c
new file mode 100644 (file)
index 0000000..2e83c37
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include <mach/mach.h>
+#include <mach/mach_error.h>
+
+#include <SystemConfiguration/SCD.h>
+#include "config.h"            /* MiG generated file */
+#include "SCDPrivate.h"
+
+
+SCDStatus
+SCDTouch(SCDSessionRef session, CFStringRef key)
+{
+       SCDSessionPrivateRef    sessionPrivate = (SCDSessionPrivateRef)session;
+       kern_return_t           status;
+       CFDataRef               xmlKey;         /* serialized key */
+       xmlData_t               myKeyRef;
+       CFIndex                 myKeyLen;
+       SCDStatus               scd_status;
+
+       SCDLog(LOG_DEBUG, CFSTR("SCDTouch:"));
+       SCDLog(LOG_DEBUG, CFSTR("  key = %@"), key);
+
+       if (key == NULL) {
+               return SCD_INVALIDARGUMENT;     /* no key specified */
+       }
+
+       if ((session == NULL) || (sessionPrivate->server == MACH_PORT_NULL)) {
+               return SCD_NOSESSION;           /* you can't do anything with a closed session */
+       }
+
+       /* serialize the key */
+       xmlKey = CFPropertyListCreateXMLData(NULL, key);
+       myKeyRef = (xmlData_t)CFDataGetBytePtr(xmlKey);
+       myKeyLen = CFDataGetLength(xmlKey);
+
+       /* send the key to the server */
+       status = configtouch(sessionPrivate->server,
+                            myKeyRef,
+                            myKeyLen,
+                            (int *)&scd_status);
+
+       /* clean up */
+       CFRelease(xmlKey);
+
+       if (status != KERN_SUCCESS) {
+               if (status != MACH_SEND_INVALID_DEST)
+                       SCDLog(LOG_DEBUG, CFSTR("configtouch(): %s"), mach_error_string(status));
+               (void) mach_port_destroy(mach_task_self(), sessionPrivate->server);
+               sessionPrivate->server = MACH_PORT_NULL;
+               return SCD_NOSERVER;
+       }
+
+       return scd_status;
+}
diff --git a/SystemConfiguration.fproj/SCDUnlock.c b/SystemConfiguration.fproj/SCDUnlock.c
new file mode 100644 (file)
index 0000000..c1540b9
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include <mach/mach.h>
+#include <mach/mach_error.h>
+
+#include <SystemConfiguration/SCD.h>
+#include "config.h"            /* MiG generated file */
+#include "SCDPrivate.h"
+
+
+SCDStatus
+SCDUnlock(SCDSessionRef session)
+{
+       SCDSessionPrivateRef    sessionPrivate = (SCDSessionPrivateRef)session;
+       kern_return_t           status;
+       SCDStatus               scd_status;
+
+       SCDLog(LOG_DEBUG, CFSTR("SCDUnlock:"));
+
+       if (session == NULL) {
+               return SCD_NOSESSION;           /* you can't do anything without closed session */
+       }
+
+       scd_status = (sessionPrivate->server == MACH_PORT_NULL) ? SCD_NOSESSION : SCD_OK;
+
+       if (scd_status == SCD_OK) {
+               /* (attempt to) release the servers lock */
+               status = configunlock(sessionPrivate->server, (int *)&scd_status);
+
+               if (status != KERN_SUCCESS) {
+                       if (status != MACH_SEND_INVALID_DEST)
+                               SCDLog(LOG_DEBUG, CFSTR("configunlock(): %s"), mach_error_string(status));
+                       (void) mach_port_destroy(mach_task_self(), sessionPrivate->server);
+                       sessionPrivate->server = MACH_PORT_NULL;
+                       return SCD_NOSERVER;
+               }
+       }
+
+       return scd_status;
+}
diff --git a/SystemConfiguration.fproj/SCNetwork.c b/SystemConfiguration.fproj/SCNetwork.c
new file mode 100644 (file)
index 0000000..9c24f2e
--- /dev/null
@@ -0,0 +1,1235 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include <SystemConfiguration/SystemConfiguration.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <netdb.h>
+#include <resolv.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <net/if.h>
+
+#include "ppp.h"
+
+
+static int
+inet_atonCF(CFStringRef cfStr, struct in_addr *addr)
+{
+       char    cStr[sizeof("255.255.255.255")];
+
+       if (!CFStringGetCString(cfStr, cStr, sizeof(cStr), kCFStringEncodingMacRoman)) {
+               return 0;
+       }
+
+       return inet_aton(cStr, addr);
+}
+
+
+/*
+ * Function: parse_component
+ * Purpose:
+ *   Given a string 'key' and a string prefix 'prefix',
+ *   return the next component in the slash '/' separated
+ *   key.
+ *
+ * Examples:
+ * 1. key = "a/b/c" prefix = "a/"
+ *    returns "b"
+ * 2. key = "a/b/c" prefix = "a/b/"
+ *    returns "c"
+ */
+static CFStringRef
+parse_component(CFStringRef key, CFStringRef prefix)
+{
+       CFMutableStringRef      comp;
+       CFRange                 range;
+
+       if (CFStringHasPrefix(key, prefix) == FALSE) {
+               return NULL;
+       }
+       comp = CFStringCreateMutableCopy(NULL, 0, key);
+       CFStringDelete(comp, CFRangeMake(0, CFStringGetLength(prefix)));
+       range = CFStringFind(comp, CFSTR("/"), 0);
+       if (range.location == kCFNotFound) {
+               return comp;
+       }
+       range.length = CFStringGetLength(comp) - range.location;
+       CFStringDelete(comp, range);
+       return comp;
+}
+
+
+/*
+ * return a dictionary of configured services.
+ */
+static CFDictionaryRef
+getServices(SCDSessionRef session)
+{
+       CFArrayRef              defined         = NULL;
+       int                     i;
+       CFStringRef             key;
+       CFStringRef             prefix;
+       CFMutableDictionaryRef  services;
+       SCDStatus               status;
+
+       prefix = SCDKeyCreate(CFSTR("%@/%@/%@/"),
+                             kSCCacheDomainSetup,
+                             kSCCompNetwork,
+                             kSCCompService);
+
+       services = CFDictionaryCreateMutable(NULL,
+                                            0,
+                                            &kCFTypeDictionaryKeyCallBacks,
+                                            &kCFTypeDictionaryValueCallBacks);
+
+       key = SCDKeyCreateNetworkServiceEntity(kSCCacheDomainSetup,
+                                              kSCCompAnyRegex,
+                                              kSCEntNetIPv4);
+       status = SCDList(session, key, kSCDRegexKey, &defined);
+       CFRelease(key);
+       if (status != SCD_OK) {
+               goto done;
+       }
+
+       for (i = 0; i < CFArrayGetCount(defined); i++) {
+               CFDictionaryRef         if_dict;
+               SCDHandleRef            if_handle       = NULL;
+               CFDictionaryRef         ip_dict;
+               SCDHandleRef            ip_handle       = NULL;
+               boolean_t               isPPP           = FALSE;
+               CFDictionaryRef         ppp_dict;
+               SCDHandleRef            ppp_handle      = NULL;
+               CFMutableDictionaryRef  sDict           = NULL;
+               CFStringRef             sid             = NULL;
+
+               key  = CFArrayGetValueAtIndex(defined, i);
+
+               /* get IPv4 dictionary for service */
+               status = SCDGet(session, key, &ip_handle);
+               if (status != SCD_OK) {
+                       /* if service was removed behind our back */
+                       goto nextService;
+               }
+               ip_dict = SCDHandleGetData(ip_handle);
+
+               sDict = CFDictionaryCreateMutableCopy(NULL, 0, ip_dict);
+
+               /* add keys from the service's Interface dictionary */
+               sid = parse_component(key, prefix);
+               if (sid == NULL) {
+                       goto nextService;
+               }
+
+               key = SCDKeyCreateNetworkServiceEntity(kSCCacheDomainSetup,
+                                                      sid,
+                                                      kSCEntNetInterface);
+               status = SCDGet(session, key, &if_handle);
+               CFRelease(key);
+               if (status != SCD_OK) {
+                       goto nextService;
+               }
+               if_dict = SCDHandleGetData(if_handle);
+
+               /* check the interface "Type", "SubType", and "DeviceName" */
+               if (CFDictionaryGetValueIfPresent(if_dict,
+                                                 kSCPropNetInterfaceType,
+                                                 (void **)&key)) {
+                       CFDictionaryAddValue(sDict, kSCPropNetInterfaceType, key);
+                       isPPP = CFEqual(key, kSCValNetInterfaceTypePPP);
+               }
+               if (CFDictionaryGetValueIfPresent(if_dict,
+                                                 kSCPropNetInterfaceSubType,
+                                                 (void **)&key)) {
+                       CFDictionaryAddValue(sDict, kSCPropNetInterfaceSubType, key);
+               }
+
+               if (isPPP) {
+                       key = SCDKeyCreateNetworkServiceEntity(kSCCacheDomainSetup,
+                                                              sid,
+                                                              kSCEntNetPPP);
+                       status = SCDGet(session, key, &ppp_handle);
+                       CFRelease(key);
+                       if (status != SCD_OK) {
+                               goto nextService;
+                       }
+                       ppp_dict = SCDHandleGetData(ppp_handle);
+
+                       /* get Dial-on-Traffic flag */
+                       if (CFDictionaryGetValueIfPresent(ppp_dict,
+                                                         kSCPropNetPPPDialOnDemand,
+                                                         (void **)&key)) {
+                               CFDictionaryAddValue(sDict, kSCPropNetPPPDialOnDemand, key);
+                       }
+               }
+
+               CFDictionaryAddValue(services, sid, sDict);
+
+       nextService:
+
+               if (sid)        CFRelease(sid);
+               if (if_handle)  SCDHandleRelease(if_handle);
+               if (ip_handle)  SCDHandleRelease(ip_handle);
+               if (ppp_handle) SCDHandleRelease(ppp_handle);
+               if (sDict)      CFRelease(sDict);
+       }
+
+    done:
+
+       if (defined)    CFRelease(defined);
+       CFRelease(prefix);
+
+       return services;
+}
+
+
+/*
+ * return a dictionary of configured interfaces.
+ */
+static CFDictionaryRef
+getInterfaces(SCDSessionRef session)
+{
+       CFMutableArrayRef       defined         = NULL;
+       int                     i;
+       CFStringRef             key;
+       CFMutableDictionaryRef  interfaces;
+       CFStringRef             prefix;
+       SCDStatus               status;
+
+       prefix = SCDKeyCreate(CFSTR("%@/%@/%@/"),
+                             kSCCacheDomainState,
+                             kSCCompNetwork,
+                             kSCCompInterface);
+
+       interfaces = CFDictionaryCreateMutable(NULL,
+                                              0,
+                                              &kCFTypeDictionaryKeyCallBacks,
+                                              &kCFTypeDictionaryValueCallBacks);
+
+       key = SCDKeyCreateNetworkInterfaceEntity(kSCCacheDomainState,
+                                                kSCCompAnyRegex,
+                                                kSCEntNetIPv4);
+       status = SCDList(session, key, kSCDRegexKey, &defined);
+       CFRelease(key);
+       if (status != SCD_OK) {
+               goto done;
+       }
+
+       for (i=0; i<CFArrayGetCount(defined); i++) {
+               CFStringRef             iid             = NULL;
+               CFDictionaryRef         ip_dict;
+               SCDHandleRef            ip_handle       = NULL;
+
+               key  = CFArrayGetValueAtIndex(defined, i);
+
+               /* get IPv4 dictionary for service */
+               status = SCDGet(session, key, &ip_handle);
+               if (status != SCD_OK) {
+                       /* if interface was removed behind our back */
+                       goto nextIF;
+               }
+               ip_dict = SCDHandleGetData(ip_handle);
+
+               iid = parse_component(key, prefix);
+               if (iid == NULL) {
+                       goto nextIF;
+               }
+
+               CFDictionaryAddValue(interfaces, iid, ip_dict);
+
+           nextIF :
+
+               if (iid)        CFRelease(iid);
+               if (ip_handle)  SCDHandleRelease(ip_handle);
+       }
+
+    done:
+
+       if (defined)    CFRelease(defined);
+       CFRelease(prefix);
+       return interfaces;
+}
+
+
+/*
+ * return an array of interface names based on a specified service order.
+ */
+static CFArrayRef
+getInterfaceOrder(CFDictionaryRef      interfaces,
+                 CFArrayRef            serviceOrder,
+                 CFNumberRef           pppOverridePrimary)
+{
+       CFIndex                 i;
+       CFIndex                 iCnt;
+       CFMutableArrayRef       iKeys;
+       void                    **keys;
+       CFMutableArrayRef       order   = NULL;
+       CFArrayRef              tKeys;
+
+       order = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+
+       iCnt = CFDictionaryGetCount(interfaces);
+       keys = CFAllocatorAllocate(NULL, iCnt * sizeof(CFStringRef), 0);
+       CFDictionaryGetKeysAndValues(interfaces, keys, NULL);
+       tKeys = CFArrayCreate(NULL, keys, iCnt, &kCFTypeArrayCallBacks);
+       CFAllocatorDeallocate(NULL, keys);
+       iKeys = CFArrayCreateMutableCopy(NULL, 0, tKeys);
+       CFRelease(tKeys);
+
+       for (i = 0; serviceOrder && i < CFArrayGetCount(serviceOrder); i++) {
+               CFIndex         j;
+               CFStringRef     oSID;
+
+               oSID = CFArrayGetValueAtIndex(serviceOrder, i);
+               for (j=0; j<CFArrayGetCount(iKeys); j++) {
+                       CFDictionaryRef iDict;
+                       CFStringRef     iKey;
+                       CFStringRef     iSID;
+                       CFArrayRef      iSIDs;
+                       CFIndex         k;
+                       boolean_t       match   = FALSE;
+
+                       iKey  = CFArrayGetValueAtIndex(iKeys, j);
+                       iDict = CFDictionaryGetValue(interfaces, iKey);
+
+                       iSIDs = CFDictionaryGetValue(iDict, kSCCachePropNetServiceIDs);
+                       for (k = 0; iSIDs && k < CFArrayGetCount(iSIDs); k++) {
+                               iSID = CFArrayGetValueAtIndex(iSIDs, k);
+                               if (CFEqual(oSID, iSID)) {
+                                       match = TRUE;
+                                       break;
+                               }
+                       }
+
+                       if (match) {
+                               /* if order ServiceID is associated with this interface */
+                               CFArrayAppendValue(order, iKey);
+                               CFArrayRemoveValueAtIndex(iKeys, j);
+                               break;
+                       }
+               }
+       }
+
+       for (i = 0; i < CFArrayGetCount(iKeys); i++) {
+               CFStringRef     iKey;
+
+               iKey = CFArrayGetValueAtIndex(iKeys, i);
+               CFArrayAppendValue(order, iKey);
+       }
+
+       CFRelease(iKeys);
+       return order;
+}
+
+
+static boolean_t
+getAddresses(CFDictionaryRef   iDict,
+            CFIndex            *nAddrs,
+            CFArrayRef         *addrs,
+            CFArrayRef         *masks,
+            CFArrayRef         *dests)
+{
+       *addrs = CFDictionaryGetValue(iDict, kSCPropNetIPv4Addresses);
+       *masks = CFDictionaryGetValue(iDict, kSCPropNetIPv4SubnetMasks);
+       *dests = CFDictionaryGetValue(iDict, kSCPropNetIPv4DestAddresses);
+
+       if ((*addrs == NULL) ||
+           ((*nAddrs = CFArrayGetCount(*addrs)) == 0)) {
+               /* sorry, no addresses */
+               return FALSE;
+       }
+
+       if ((*masks && *dests) ||
+           (*masks == NULL) && (*dests == NULL)) {
+               /*
+                * sorry, we expect to have "SubnetMasks" or
+                * "DestAddresses" (not both) and if the count
+                * must match the number of "Addresses".
+                */
+               return FALSE;
+       }
+
+       if (*masks && (*nAddrs != CFArrayGetCount(*masks))) {
+               /* if we don't like the netmasks */
+               return FALSE;
+       }
+
+       if (*dests &&  (*nAddrs != CFArrayGetCount(*dests))) {
+               /* if we don't like the destaddresses */
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static SCNStatus
+checkAddress(SCDSessionRef             session,
+            const struct sockaddr      *address,
+            const int                  addrlen,
+            CFDictionaryRef            services,
+            CFDictionaryRef            interfaces,
+            CFArrayRef                 interfaceOrder,
+            struct in_addr             *defaultRoute,
+            int                        *flags,
+            const char                 **errorMessage)
+{
+       CFIndex                 i;
+       struct ifreq            ifr;
+       CFIndex                 iCnt;
+       CFStringRef             iKey            = NULL;
+       CFStringRef             iType           = NULL;
+       void                    **keys;
+       int                     pppRef          = -1;
+       SCNStatus               scn_status      = SCN_REACHABLE_UNKNOWN;
+       CFIndex                 sCnt;
+       CFMutableArrayRef       sKeys           = NULL;
+       CFStringRef             sID             = NULL;
+       CFArrayRef              sIDs            = NULL;
+       CFArrayRef              sList           = NULL;
+       int                     sock            = -1;
+       CFStringRef             sKey            = NULL;
+       CFDictionaryRef         sDict           = NULL;
+       CFArrayRef              tKeys;
+
+       if (flags != NULL) {
+               *flags = 0;
+       }
+
+       if (address == NULL) {
+               return SCN_REACHABLE_NO;
+       }
+
+       sCnt = CFDictionaryGetCount(services);
+       keys = CFAllocatorAllocate(NULL, sCnt * sizeof(CFStringRef), 0);
+       CFDictionaryGetKeysAndValues(services, keys, NULL);
+       tKeys = CFArrayCreate(NULL, keys, sCnt, &kCFTypeArrayCallBacks);
+       CFAllocatorDeallocate(NULL, keys);
+       sKeys = CFArrayCreateMutableCopy(NULL, 0, tKeys);
+       CFRelease(tKeys);
+
+       if (address->sa_family == AF_INET) {
+               struct sockaddr_in      *sin = (struct sockaddr_in *)address;
+
+#ifdef DEBUG
+               if (SCDOptionGet(session, kSCDOptionDebug))
+                       SCDLog(LOG_INFO, CFSTR("checkAddress(%s)"), inet_ntoa(sin->sin_addr));
+#endif /* DEBUG */
+               /*
+                * Check for loopback address
+                */
+               if (ntohl(sin->sin_addr.s_addr) == ntohl(INADDR_LOOPBACK)) {
+                       /* if asking about the loopback address */
+#ifdef DEBUG
+                       if (SCDOptionGet(session, kSCDOptionDebug))
+                               SCDLog(LOG_INFO, CFSTR("  isReachable via loopback"));
+#endif /* DEBUG */
+                       scn_status = SCN_REACHABLE_YES;
+                       goto done;
+               }
+
+               /*
+                * Check if the address is on one of the subnets
+                * associated with our active IPv4 interfaces
+                */
+               iCnt = CFArrayGetCount(interfaceOrder);
+               for (i=0; i<iCnt; i++) {
+                       CFArrayRef              addrs;
+                       CFArrayRef              dests;
+                       CFDictionaryRef         iDict;
+                       CFIndex                 j;
+                       CFArrayRef              masks;
+                       CFIndex                 nAddrs  = 0;
+
+                       iKey  = CFArrayGetValueAtIndex(interfaceOrder, i);
+                       iDict = CFDictionaryGetValue(interfaces, iKey);
+
+                       /* remove active services */
+                       sIDs = CFDictionaryGetValue(iDict, kSCCachePropNetServiceIDs);
+                       for (j = 0; sIDs && j < CFArrayGetCount(sIDs); j++) {
+                               CFIndex         k;
+                               CFStringRef     sID;
+
+                               sID = CFArrayGetValueAtIndex(sIDs, j);
+                               k   = CFArrayGetFirstIndexOfValue(sKeys,
+                                                                 CFRangeMake(0, CFArrayGetCount(sKeys)),
+                                                                 sID);
+                               if (k != -1) {
+                                       CFArrayRemoveValueAtIndex(sKeys, k);
+                               }
+                       }
+
+                       if (!getAddresses(iDict, &nAddrs, &addrs, &masks, &dests)) {
+                               /* if no addresses to check */
+                               continue;
+                       }
+
+                       for (j=0; j<nAddrs; j++) {
+                               struct in_addr  ifAddr;
+
+                               if (inet_atonCF(CFArrayGetValueAtIndex(addrs, j),
+                                               &ifAddr) == 0) {
+                                       /* if Addresses string is invalid */
+                                       break;
+                               }
+
+                               if (masks) {
+                                       struct in_addr  ifMask;
+
+                                       if (inet_atonCF(CFArrayGetValueAtIndex(masks, j),
+                                                       &ifMask) == 0) {
+                                               /* if SubnetMask string is invalid */
+                                               break;
+                                       }
+
+                                       if ((ntohl(ifAddr.s_addr)        & ntohl(ifMask.s_addr)) ==
+                                           (ntohl(sin->sin_addr.s_addr) & ntohl(ifMask.s_addr))) {
+                                               /* the requested address is on this subnet */
+#ifdef DEBUG
+                                               if (SCDOptionGet(session, kSCDOptionDebug))
+                                                       SCDLog(LOG_INFO, CFSTR("  isReachable (my subnet)"));
+#endif /* DEBUG */
+                                               scn_status = SCN_REACHABLE_YES;
+                                               goto checkInterface;
+                                       }
+                               } else {
+                                       struct in_addr  destAddr;
+
+                                       /* check remote address */
+                                       if (inet_atonCF(CFArrayGetValueAtIndex(dests, j),
+                                                       &destAddr) == 0) {
+                                               /* if DestAddresses string is invalid */
+                                               break;
+                                       }
+
+                                       /* check local address */
+                                       if (ntohl(sin->sin_addr.s_addr) == ntohl(ifAddr.s_addr)) {
+                                               /* the address is our side of the link */
+#ifdef DEBUG
+                                               if (SCDOptionGet(session, kSCDOptionDebug))
+                                                       SCDLog(LOG_INFO, CFSTR("  isReachable (my local address)"));
+#endif /* DEBUG */
+                                               scn_status = SCN_REACHABLE_YES;
+                                               goto checkInterface;
+                                       }
+
+                                       if (ntohl(sin->sin_addr.s_addr) == ntohl(destAddr.s_addr)) {
+                                               /* the address is the other side of the link */
+#ifdef DEBUG
+                                               if (SCDOptionGet(session, kSCDOptionDebug))
+                                                       SCDLog(LOG_INFO, CFSTR("  isReachable (my remote address)"));
+#endif /* DEBUG */
+                                               scn_status = SCN_REACHABLE_YES;
+                                               goto checkInterface;
+                                       }
+                               }
+                       }
+               }
+
+               /*
+                * Check if the address is accessible via the "default" route.
+                */
+               for (i=0; i<iCnt; i++) {
+                       CFArrayRef              addrs;
+                       CFArrayRef              dests;
+                       CFDictionaryRef         iDict;
+                       CFIndex                 j;
+                       CFArrayRef              masks;
+                       CFIndex                 nAddrs  = 0;
+
+                       iKey  = CFArrayGetValueAtIndex(interfaceOrder, i);
+                       iDict = CFDictionaryGetValue(interfaces, iKey);
+
+                       if (!getAddresses(iDict, &nAddrs, &addrs, &masks, &dests)) {
+                               /* if no addresses to check */
+                               continue;
+                       }
+
+                       for (j=0; defaultRoute && j<nAddrs; j++) {
+                               if (masks) {
+                                       struct in_addr  ifAddr;
+                                       struct in_addr  ifMask;
+
+                                       if (inet_atonCF(CFArrayGetValueAtIndex(addrs, j),
+                                                       &ifAddr) == 0) {
+                                               /* if Addresses string is invalid */
+                                               break;
+                                       }
+
+                                       if (inet_atonCF(CFArrayGetValueAtIndex(masks, j),
+                                                       &ifMask) == 0) {
+                                               /* if SubnetMasks string is invalid */
+                                               break;
+                                       }
+
+                                       if ((ntohl(ifAddr.s_addr)        & ntohl(ifMask.s_addr)) ==
+                                           (ntohl(defaultRoute->s_addr) & ntohl(ifMask.s_addr))) {
+                                               /* the requested address is on this subnet */
+#ifdef DEBUG
+                                               if (SCDOptionGet(session, kSCDOptionDebug))
+                                                       SCDLog(LOG_INFO, CFSTR("  isReachable via default route (my subnet)"));
+#endif /* DEBUG */
+                                               scn_status = SCN_REACHABLE_YES;
+                                               goto checkInterface;
+                                       }
+                               } else {
+                                       struct in_addr  destAddr;
+
+                                       /* check remote address */
+                                       if (inet_atonCF(CFArrayGetValueAtIndex(dests, j),
+                                                       &destAddr) == 0) {
+                                               /* if DestAddresses string is invalid */
+                                               break;
+                                       }
+
+                                       if (ntohl(destAddr.s_addr) == ntohl(defaultRoute->s_addr)) {
+                                               /* the address is the other side of the link */
+#ifdef DEBUG
+                                               if (SCDOptionGet(session, kSCDOptionDebug))
+                                                       SCDLog(LOG_INFO, CFSTR("  isReachable via default route (my remote address)"));
+#endif /* DEBUG */
+                                               scn_status = SCN_REACHABLE_YES;
+                                               goto checkInterface;
+                                       }
+                               }
+                       }
+               }
+
+               /*
+                * Check the not active (but configured) IPv4 services
+                */
+               sCnt = CFArrayGetCount(sKeys);
+               for (i=0; i<sCnt; i++) {
+                       CFArrayRef              addrs;
+                       CFStringRef             configMethod    = NULL;
+                       CFArrayRef              dests;
+                       CFIndex                 j;
+                       CFArrayRef              masks;
+                       CFIndex                 nAddrs          = 0;
+
+                       sKey  = CFArrayGetValueAtIndex(sKeys, i);
+                       sDict = CFDictionaryGetValue(services, sKey);
+
+                       /*
+                        * check configured network addresses
+                        */
+                       for (j=0; j<nAddrs; j++) {
+                               struct in_addr  ifAddr;
+
+                               if (inet_atonCF(CFArrayGetValueAtIndex(addrs, j),
+                                               &ifAddr) == 0) {
+                                       /* if Addresses string is invalid */
+                                       break;
+                               }
+
+                               if (masks) {
+                                       struct in_addr  ifMask;
+
+                                       /* check address/netmask */
+                                       if (inet_atonCF(CFArrayGetValueAtIndex(masks, j),
+                                                       &ifMask) == 0) {
+                                               /* if SubnetMasks string is invalid */
+                                               break;
+                                       }
+
+                                       if ((ntohl(ifAddr.s_addr)        & ntohl(ifMask.s_addr)) !=
+                                           (ntohl(sin->sin_addr.s_addr) & ntohl(ifMask.s_addr))) {
+                                               /* the requested address is on this subnet */
+#ifdef DEBUG
+                                               if (SCDOptionGet(session, kSCDOptionDebug))
+                                                       SCDLog(LOG_INFO, CFSTR("  is configured w/static info (my subnet)"));
+#endif /* DEBUG */
+                                               goto checkService;
+                                       }
+                               } else {
+                                       struct in_addr  destAddr;
+
+                                       /* check remote address */
+                                       if (inet_atonCF(CFArrayGetValueAtIndex(dests, j),
+                                                       &destAddr) == 0) {
+                                               /* if DestAddresses string is invalid */
+                                               break;
+                                       }
+
+                                       /* check local address */
+                                       if (ntohl(sin->sin_addr.s_addr) == ntohl(ifAddr.s_addr)) {
+                                               /* the address is our side of the link */
+#ifdef DEBUG
+                                               if (SCDOptionGet(session, kSCDOptionDebug))
+                                                       SCDLog(LOG_INFO, CFSTR("  is configured w/static info (my local address)"));
+#endif /* DEBUG */
+                                               goto checkService;
+                                       }
+
+                                       if (ntohl(sin->sin_addr.s_addr) == ntohl(destAddr.s_addr)) {
+                                               /* the address is the other side of the link */
+#ifdef DEBUG
+                                               if (SCDOptionGet(session, kSCDOptionDebug))
+                                                       SCDLog(LOG_INFO, CFSTR("  is configured w/static info (my remote address)"));
+#endif /* DEBUG */
+                                               goto checkService;
+                                       }
+                               }
+                       }
+
+                       /*
+                        * check for dynamic (i.e. not manual) configuration
+                        * method.
+                        */
+                       if (CFDictionaryGetValueIfPresent(sDict,
+                                                         kSCPropNetIPv4ConfigMethod,
+                                                         (void **)&configMethod) &&
+                           !CFEqual(configMethod, kSCValNetIPv4ConfigMethodManual)) {
+                               /* if only we were "connected" */
+#ifdef DEBUG
+                               if (SCDOptionGet(session, kSCDOptionDebug))
+                                       SCDLog(LOG_INFO, CFSTR("  is configured w/dynamic addressing"));
+#endif /* DEBUG */
+                               goto checkService;
+                       }
+               }
+
+#ifdef DEBUG
+               if (SCDOptionGet(session, kSCDOptionDebug))
+                       SCDLog(LOG_INFO, CFSTR("  cannot be reached"));
+#endif /* DEBUG */
+               scn_status = SCN_REACHABLE_NO;
+               goto done;
+
+       } else {
+               /*
+                * if no code for this address family (yet)
+                */
+               SCDSessionLog(session,
+                             LOG_ERR,
+                             CFSTR("checkAddress(): unexpected address family %d"),
+                             address->sa_family);
+               if (errorMessage != NULL) {
+                       *errorMessage = "unexpected address family";
+               }
+               goto done;
+       }
+
+       goto done;
+
+    checkInterface :
+
+       /*
+        * We have an interface which "claims" to be a valid path
+        * off of the system.  Check to make sure that this isn't
+        * a dial-on-demand PPP link that isn't connected yet.
+        */
+       if (sIDs) {
+               CFNumberRef     num;
+               CFDictionaryRef sDict;
+
+               /* attempt to get the interface type from the first service */
+               sID   = CFArrayGetValueAtIndex(sIDs, 0);
+               sDict = CFDictionaryGetValue(services, sID);
+               if (sDict) {
+                       iType = CFDictionaryGetValue(sDict, kSCPropNetInterfaceType);
+               }
+
+               if (!iType) {
+                       /* if we don't know the interface type */
+                       goto done;
+               }
+
+               if (!CFEqual(iType, kSCValNetInterfaceTypePPP)) {
+                       /* if not a ppp interface */
+                       goto done;
+               }
+
+               num = CFDictionaryGetValue(sDict, kSCPropNetPPPDialOnDemand);
+               if (num) {
+                       int     dialOnDemand;
+
+                       CFNumberGetValue(num, kCFNumberIntType, &dialOnDemand); 
+                       if (flags && (dialOnDemand != 0)) {
+                               *flags |= kSCNFlagsConnectionAutomatic;
+                       }
+                       
+               }
+       } else if (!CFStringHasPrefix(iKey, CFSTR("ppp"))) {
+               /* if not a ppp interface */
+               goto done;
+       }
+
+       if (flags != NULL) {
+               *flags |= kSCNFlagsTransientConnection;
+       }
+
+       if (sID) {
+               u_int32_t               pppLink;
+               struct ppp_status       *pppLinkStatus;
+               int                     pppStatus;
+
+               /*
+                * The service ID is available, ask the PPP controller
+                * for the extended status.
+                */
+               pppStatus = PPPInit(&pppRef);
+               if (pppStatus != 0) {
+#ifdef DEBUG
+                       if (SCDOptionGet(session, kSCDOptionDebug))
+                               SCDLog(LOG_DEBUG, CFSTR("  PPPInit() failed: status=%d"), pppStatus);
+#endif /* DEBUG */
+                       scn_status = SCN_REACHABLE_UNKNOWN;
+                       if (errorMessage != NULL) {
+                               *errorMessage = "PPPInit() failed";
+                       }
+                       goto done;
+               }
+
+               pppStatus = PPPGetLinkByServiceID(pppRef, sID, &pppLink);
+               if (pppStatus != 0) {
+#ifdef DEBUG
+                       if (SCDOptionGet(session, kSCDOptionDebug))
+                               SCDLog(LOG_DEBUG, CFSTR("  PPPGetLinkByServiceID() failed: status=%d"), pppStatus);
+#endif /* DEBUG */
+                       scn_status = SCN_REACHABLE_UNKNOWN;
+                       if (errorMessage != NULL) {
+                               *errorMessage = "PPPGetLinkByServiceID() failed";
+                       }
+                       goto done;
+               }
+
+               pppStatus = PPPStatus(pppRef, pppLink, &pppLinkStatus);
+               if (pppStatus != 0) {
+#ifdef DEBUG
+                       if (SCDOptionGet(session, kSCDOptionDebug))
+                               SCDLog(LOG_DEBUG, CFSTR("  PPPStatus() failed: status=%d"), pppStatus);
+#endif /* DEBUG */
+                       scn_status = SCN_REACHABLE_UNKNOWN;
+                       if (errorMessage != NULL) {
+                               *errorMessage = "PPPStatus() failed";
+                       }
+                       goto done;
+               }
+#ifdef DEBUG
+               if (SCDOptionGet(session, kSCDOptionDebug))
+                       SCDLog(LOG_DEBUG, CFSTR("  PPP link status = %d"), pppLinkStatus->status);
+#endif /* DEBUG */
+               switch (pppLinkStatus->status) {
+                       case PPP_RUNNING :
+                               /* if we're really UP and RUNNING */
+                               break;
+                       case PPP_IDLE :
+                               /* if we're not connected at all */
+#ifdef DEBUG
+                               if (SCDOptionGet(session, kSCDOptionDebug))
+                                       SCDLog(LOG_INFO, CFSTR("  PPP link idle, dial-on-traffic to connect"));
+#endif /* DEBUG */
+                               scn_status = SCN_REACHABLE_CONNECTION_REQUIRED;
+                               break;
+                       default :
+                               /* if we're in the process of [dis]connecting */
+#ifdef DEBUG
+                               if (SCDOptionGet(session, kSCDOptionDebug))
+                                       SCDLog(LOG_INFO, CFSTR("  PPP link, connection in progress"));
+#endif /* DEBUG */
+                               scn_status = SCN_REACHABLE_CONNECTION_REQUIRED;
+                               break;
+               }
+               CFAllocatorDeallocate(NULL, pppLinkStatus);
+       } else {
+               /*
+                * The service ID is not available, check the interfaces
+                * UP and RUNNING flags.
+                */
+               bzero(&ifr, sizeof(ifr));
+               if (!CFStringGetCString(iKey,
+                                       (char *)&ifr.ifr_name,
+                                       sizeof(ifr.ifr_name),
+                                       kCFStringEncodingMacRoman)) {
+                       scn_status = SCN_REACHABLE_UNKNOWN;
+                       if (errorMessage != NULL) {
+                               *errorMessage = "could not convert interface name to C string";
+                       }
+                       goto done;
+               }
+
+               sock = socket(AF_INET, SOCK_DGRAM, 0);
+               if (sock == -1) {
+                       scn_status = SCN_REACHABLE_UNKNOWN;
+                       if (errorMessage != NULL) {
+                               *errorMessage = strerror(errno);
+                       }
+                       goto done;
+               }
+
+               if (ioctl(sock, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
+                       scn_status = SCN_REACHABLE_UNKNOWN;
+                       if (errorMessage != NULL) {
+                               *errorMessage = strerror(errno);
+                       }
+                       goto done;
+               }
+
+#ifdef DEBUG
+               if (SCDOptionGet(session, kSCDOptionDebug))
+                       SCDLog(LOG_INFO, CFSTR("  flags for %s == 0x%hx"), ifr.ifr_name, ifr.ifr_flags);
+#endif /* DEBUG */
+               if ((ifr.ifr_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
+                       if ((ifr.ifr_flags & IFF_UP) == IFF_UP) {
+                               /* if we're "up" but not "running" */
+#ifdef DEBUG
+                               if (SCDOptionGet(session, kSCDOptionDebug))
+                                       SCDLog(LOG_INFO, CFSTR("  up & not running, dial-on-traffic to connect"));
+#endif /* DEBUG */
+                               scn_status = SCN_REACHABLE_CONNECTION_REQUIRED;
+                               if (flags != NULL) {
+                                       *flags |= kSCNFlagsConnectionAutomatic;
+                               }
+                       } else {
+                               /* if we're not "up" and "running" */
+#ifdef DEBUG
+                               if (SCDOptionGet(session, kSCDOptionDebug))
+                                       SCDLog(LOG_INFO, CFSTR("  not up & running, connection required"));
+#endif /* DEBUG */
+                               scn_status = SCN_REACHABLE_CONNECTION_REQUIRED;
+                       }
+                       goto done;
+               }
+       }
+
+       goto done;
+
+    checkService :
+
+       /*
+        * We have a service which "claims" to be a potential path
+        * off of the system.  Check to make sure that this is a
+        * type of PPP link before claiming it's viable.
+        */
+       if (sDict &&
+           CFDictionaryGetValueIfPresent(sDict,
+                                         kSCPropNetInterfaceType,
+                                         (void **)&iType) &&
+           !CFEqual(iType, kSCValNetInterfaceTypePPP)) {
+               /* no path if this not a ppp interface */
+#ifdef DEBUG
+               if (SCDOptionGet(session, kSCDOptionDebug))
+                       SCDLog(LOG_INFO, CFSTR("  cannot be reached"));
+#endif /* DEBUG */
+               scn_status = SCN_REACHABLE_NO;
+               goto done;
+       }
+
+       scn_status = SCN_REACHABLE_CONNECTION_REQUIRED;
+       if (flags != NULL) {
+               *flags |= kSCNFlagsTransientConnection;
+       }
+
+    done :
+
+       if (sKeys)              CFRelease(sKeys);
+       if (sList)              CFRelease(sList);
+       if (pppRef != -1)       (void) PPPDispose(pppRef);
+       if (sock != -1)         (void)close(sock);
+
+       return scn_status;
+}
+
+
+static void
+_IsReachableInit(SCDSessionRef session,
+       CFDictionaryRef *services,
+       CFDictionaryRef *interfaces,
+       CFArrayRef      *interfaceOrder,
+       struct in_addr  **defaultRoute)
+{
+       CFStringRef     addr;
+       CFDictionaryRef dict;
+       CFStringRef     key;
+       SCDHandleRef    handle;
+       CFNumberRef     pppOverridePrimary      = NULL;
+       CFArrayRef      serviceOrder            = NULL;
+       struct in_addr  *route                  = NULL;
+       SCDStatus       status;
+
+       /*
+        * get the ServiceOrder and PPPOverridePrimary keys
+        * from the global settings.
+        */
+       key = SCDKeyCreateNetworkGlobalEntity(kSCCacheDomainSetup, kSCEntNetIPv4);
+       status = SCDGet(session, key, &handle);
+       CFRelease(key);
+       switch (status) {
+               case SCD_OK :
+                       /* if global settings are available */
+                       dict = SCDHandleGetData(handle);
+
+                       /* get service order */
+                       if ((CFDictionaryGetValueIfPresent(dict,
+                                                          kSCPropNetServiceOrder,
+                                                          (void **)&serviceOrder) == TRUE)) {
+                               CFRetain(serviceOrder);
+                       }
+
+                       /* get PPP overrides primary flag */
+                       if ((CFDictionaryGetValueIfPresent(dict,
+                                                          kSCPropNetPPPOverridePrimary,
+                                                          (void **)&pppOverridePrimary) == TRUE)) {
+                               CFRetain(pppOverridePrimary);
+                       }
+
+                       SCDHandleRelease(handle);
+                       break;
+               case SCD_NOKEY :
+                       /* if no global settings */
+                       break;
+               default :
+                       SCDLog(LOG_ERR, CFSTR("SCDGet() failed: %s"), SCDError(status));
+                       /* XXX need to do something more with this FATAL error XXXX */
+                       goto error;
+       }
+
+       /*
+        * Get default route
+        */
+       key = SCDKeyCreateNetworkGlobalEntity(kSCCacheDomainState,
+                                             kSCEntNetIPv4);
+       status = SCDGet(session, key, &handle);
+       CFRelease(key);
+       switch (status) {
+               case SCD_OK :
+                       dict = SCDHandleGetData(handle);
+                       addr = CFDictionaryGetValue(dict, kSCPropNetIPv4Router);
+                       if (addr == NULL) {
+                               /* if no default route */
+                               break;
+                       }
+
+                       route = CFAllocatorAllocate(NULL, sizeof(struct in_addr), 0);
+                       if (inet_atonCF(addr, route) == 0) {
+                               /* if address string is invalid */
+                               CFAllocatorDeallocate(NULL, route);
+                               route = NULL;
+                               break;
+                       }
+                       *defaultRoute = route;
+
+                       break;
+               case SCD_NOKEY :
+                       /* if no default route */
+                       break;
+               default :
+                       SCDSessionLog(session,
+                                     LOG_ERR,
+                                     CFSTR("SCDGet() failed: %s"),
+                                     SCDError(status));
+                       goto error;
+       }
+       if (handle) {
+               SCDHandleRelease(handle);
+               handle = NULL;
+       }
+
+       /*
+        * get the configured services and interfaces
+        */
+       *services       = getServices  (session);
+       *interfaces     = getInterfaces(session);
+       *interfaceOrder = getInterfaceOrder(*interfaces,
+                                           serviceOrder,
+                                           pppOverridePrimary);
+
+    error :
+
+       if (serviceOrder)       CFRelease(serviceOrder);
+       if (pppOverridePrimary) CFRelease(pppOverridePrimary);
+
+#ifdef DEBUG
+       if (SCDOptionGet(session, kSCDOptionDebug)) {
+               SCDLog(LOG_NOTICE, CFSTR("interfaces     = %@"), *interfaces);
+               SCDLog(LOG_NOTICE, CFSTR("services       = %@"), *services);
+               SCDLog(LOG_NOTICE, CFSTR("interfaceOrder = %@"), *interfaceOrder);
+               SCDLog(LOG_NOTICE, CFSTR("defaultRoute   = %s"), *defaultRoute?inet_ntoa(**defaultRoute):"None");
+       }
+#endif /* DEBUG */
+       return;
+
+}
+
+
+static void
+_IsReachableFree(CFDictionaryRef       services,
+                CFDictionaryRef        interfaces,
+                CFArrayRef             interfaceOrder,
+                struct in_addr         *defaultRoute)
+{
+       if (services)           CFRelease(services);
+       if (interfaces)         CFRelease(interfaces);
+       if (interfaceOrder)     CFRelease(interfaceOrder);
+       if (defaultRoute)       CFAllocatorDeallocate(NULL, defaultRoute);
+       return;
+}
+
+
+SCNStatus
+SCNIsReachableByAddress(const struct sockaddr  *address,
+                       const int               addrlen,
+                       int                     *flags,
+                       const char              **errorMessage)
+{
+       struct in_addr  *defaultRoute   = NULL;
+       CFDictionaryRef interfaces      = NULL;
+       CFArrayRef      interfaceOrder  = NULL;
+       CFDictionaryRef services        = NULL;
+       SCDSessionRef   session         = NULL;
+       SCDStatus       scd_status;
+       SCNStatus       scn_status;
+
+       scd_status = SCDOpen(&session, CFSTR("SCNIsReachableByAddress"));
+       if (scd_status != SCD_OK) {
+               if (errorMessage != NULL) {
+                       *errorMessage = SCDError(scd_status);
+               }
+               return SCN_REACHABLE_UNKNOWN;
+       }
+
+       _IsReachableInit(session, &services, &interfaces, &interfaceOrder, &defaultRoute);
+       scn_status = checkAddress(session,
+                                 address,
+                                 addrlen,
+                                 services,
+                                 interfaces,
+                                 interfaceOrder,
+                                 defaultRoute,
+                                 flags,
+                                 errorMessage);
+       _IsReachableFree(services, interfaces, interfaceOrder, defaultRoute);
+
+       (void) SCDClose(&session);
+       return scn_status;
+}
+
+
+SCNStatus
+SCNIsReachableByName(const char                *nodename,
+                    int                *flags,
+                    const char         **errorMessage)
+{
+       struct in_addr  *defaultRoute   = NULL;
+       int             i;
+       CFDictionaryRef interfaces      = NULL;
+       CFArrayRef      interfaceOrder  = NULL;
+       SCDStatus       scd_status      = SCD_OK;
+       SCNStatus       ns_status       = SCN_REACHABLE_YES;
+       struct addrinfo *res            = NULL;
+       struct addrinfo *resP;
+       CFDictionaryRef services        = NULL;
+       SCDSessionRef   session         = NULL;
+       SCNStatus       scn_status      = SCN_REACHABLE_YES;
+
+       scd_status = SCDOpen(&session, CFSTR("SCNIsReachableByName"));
+       if (scd_status != SCD_OK) {
+               scn_status = SCN_REACHABLE_UNKNOWN;
+               if (errorMessage != NULL) {
+                       *errorMessage = SCDError(scd_status);
+               }
+               goto done;
+       }
+
+       _IsReachableInit(session, &services, &interfaces, &interfaceOrder, &defaultRoute);
+
+       /*
+        * since we don't know which name server will be consulted
+        * to resolve the specified nodename we need to check the
+        * availability of ALL name servers.
+        */
+       res_init();
+       for (i=0; i<_res.nscount; i++) {
+               ns_status = checkAddress(session,
+                                        (struct sockaddr *)&_res.nsaddr_list[i],
+                                        _res.nsaddr_list[i].sin_len,
+                                        services,
+                                        interfaces,
+                                        interfaceOrder,
+                                        defaultRoute,
+                                        flags,
+                                        errorMessage);
+               if (ns_status < scn_status) {
+                       /* return the worst case result */
+                       scn_status = ns_status;
+                       if (ns_status == SCN_REACHABLE_UNKNOWN) {
+                               /* not today */
+                               break;
+                       }
+               }
+       }
+
+       if (ns_status < SCN_REACHABLE_YES) {
+               goto done;
+       }
+
+       /*
+        * OK, all of the DNS name servers are available.  Let's
+        * first assume that the requested host is NOT available,
+        * resolve the nodename, and check its address for
+        * accessibility. We return the best status available.
+        */
+       scn_status = SCN_REACHABLE_UNKNOWN;
+
+       /*
+        * resolve the nodename into an address
+        */
+       i = getaddrinfo(nodename, NULL, NULL, &res);
+       if (i != 0) {
+               SCDSessionLog(session,
+                             LOG_ERR,
+                             CFSTR("getaddrinfo() failed: %s"),
+                             gai_strerror(i));
+               goto done;
+       }
+
+       for (resP=res; resP!=NULL; resP=resP->ai_next) {
+               ns_status = checkAddress(session,
+                                        resP->ai_addr,
+                                        resP->ai_addrlen,
+                                        services,
+                                        interfaces,
+                                        interfaceOrder,
+                                        defaultRoute,
+                                        flags,
+                                        errorMessage);
+               if (ns_status > scn_status) {
+                       /* return the best case result */
+                       scn_status = ns_status;
+                       if (ns_status == SCN_REACHABLE_YES) {
+                               /* we're in luck */
+                               break;
+                       }
+               }
+       }
+
+    done :
+
+       _IsReachableFree(services, interfaces, interfaceOrder, defaultRoute);
+       if (session)    (void)SCDClose(&session);
+       if (res)        freeaddrinfo(res);
+
+       return scn_status;
+}
diff --git a/SystemConfiguration.fproj/SCNetwork.h b/SystemConfiguration.fproj/SCNetwork.h
new file mode 100644 (file)
index 0000000..2f22ca1
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#ifndef _SCPNETWORK_H
+#define _SCPNETWORK_H
+
+#include <sys/cdefs.h>
+#include <sys/socket.h>
+
+/*!
+       @header SCPNetwork.h
+       The SystemConfiguration framework provides access to the data used
+               to configure a running system.
+
+       Specifically, the SCPNetworkXXX() API's allow an application
+               to determine the status of the systems current network
+               configuration.
+
+       The APIs provided by this framework communicate with the "configd"
+               daemon to obtain information regarding the systems current
+               configuration.
+ */
+
+/*!
+       @enum SCNStatus
+       @discussion Returned status codes from the SCNIsReachableByAddress()
+               and SCNIsReachableByName() functions.
+
+               The term "reachable" in these status codes reflects whether
+               a data packet, sent by an application into the network stack,
+               will be able to reach the destination host.
+
+               Please not that being "able" to reach the destination host
+               does not guarantee that the data packet "will" reach the
+               host.
+
+       @constant SCN_REACHABLE_UNKNOWN
+               A determination could not be made regarding the reachability
+               of the specified nodename/address.
+
+       @constant SCN_REACHABLE_NO
+               The specified nodename/address can not be reached using the
+               current network configuration.
+
+       @constant SCN_REACHABLE_CONNECTION_REQUIRED
+               The specified nodename/address can be reached using the
+               current network configuration but a connection must first
+               be established.
+
+               This status would be returned for a dialup connection which
+               was not currently active but could handle network traffic for
+               the target system.
+
+       @constant SCN_REACHABLE_YES
+               The specified nodename/address can be reached using the
+               current network configuration.
+ */
+typedef enum {
+       SCN_REACHABLE_UNKNOWN                   = -1,
+       SCN_REACHABLE_NO                        =  0,
+       SCN_REACHABLE_CONNECTION_REQUIRED       =  1,
+       SCN_REACHABLE_YES                       =  2,
+} SCNStatus;
+
+
+/*!
+       @enum SCNConnectionFlags
+       @discussion Additional flags which reflect whether a network connection
+               to the specified nodename/address is reachable, requires a
+               connection, requires some user intervention in establishing
+               the connection, and whether the calling application must initiate
+               the connection using the SCNEstablishConnection() API.
+
+       @constant kSCNFlagsTransientConnection
+               This flag indicates that the specified nodename/address can
+               be reached via a transient (e.g. PPP) connection.
+
+       @constant kSCNFlagsConnectionAutomatic
+               The specified nodename/address can be reached using the
+               current network configuration but a connection must first
+               be established.  Any traffic directed to the specified
+               name/address will initiate the connection.
+
+       @constant kSCNFlagsInterventionRequired
+               The specified nodename/address can be reached using the
+               current network configuration but a connection must first
+               be established.  In addition, some form of user intervention
+               will be required to establish this connection (e.g. providing
+               a password, authentication token, etc.).
+ */
+typedef enum {
+       kSCNFlagsTransientConnection    =  1<<0,
+       kSCNFlagsConnectionAutomatic    =  1<<1,
+       kSCNFlagsInterventionRequired   =  1<<2,
+} SCNConnectionFlags;
+
+
+__BEGIN_DECLS
+
+/*!
+       @function SCNIsReachableByAddress
+       @discussion Determines if the given network address is
+               reachable using the current network configuration.
+
+               Note: This API is not thread safe.
+       @param address Pass the network address of the desired host.
+       @param addrlen Pass the length, in bytes, of the address.
+       @param flags A pointer to memory which will be filled with a
+               set of SCNConnectionFlags related to the reachability
+               of the specified address.  If NULL, no flags will be
+               returned.
+       @param status A pointer to memory which will be filled with the
+               error status associated with any error communicating with
+               the system configuration daemon.
+       @result A constant of type SCNStatus indicating the reachability
+               of the specified node address.
+ */
+SCNStatus      SCNIsReachableByAddress (const struct sockaddr  *address,
+                                        const int              addrlen,
+                                        int                    *flags,
+                                        const char             **errorMessage);
+
+/*!
+       @function SCNIsReachableByName
+       @discussion Determines if the given network host/node name is
+               reachable using the current network configuration.
+       @param nodename Pass a node name of the desired host. This name would
+               be the same as that passed to gethostbyname() or getaddrinfo().
+       @param flags A pointer to memory which will be filled with a
+               set of SCNConnectionFlags related to the reachability
+               of the specified address.  If NULL, no flags will be
+               returned.
+       @param status A pointer to memory which will be filled with the
+               error status associated with any error communicating with
+               the system configuration daemon.
+       @result A constant of type SCNStatus indicating the reachability
+               of the specified node address.
+ */
+SCNStatus      SCNIsReachableByName    (const char             *nodename,
+                                        int                    *flags,
+                                        const char             **errorMessage);
+
+__END_DECLS
+
+#endif /* _SCPNETWORK_H */
diff --git a/SystemConfiguration.fproj/SCP.c b/SystemConfiguration.fproj/SCP.c
new file mode 100644 (file)
index 0000000..15d39f3
--- /dev/null
@@ -0,0 +1,250 @@
+/*
+ * Copyright(c) 2000 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@
+ */
+
+#include <SystemConfiguration/SystemConfiguration.h>
+#include "SCPPrivate.h"
+
+#include <fcntl.h>
+#include <pwd.h>
+#include <unistd.h>
+#include <sys/errno.h>
+#include <sys/param.h>
+
+
+static const struct scp_errmsg {
+       SCPStatus       status;
+       char            *message;
+} scp_errmsgs[] = {
+       { SCP_OK,               "Success!" },
+       { SCP_BUSY,             "Configuration daemon busy" },
+       { SCP_NEEDLOCK,         "Lock required for this operation" },
+       { SCP_EACCESS,          "Permission denied (must be root to obtain lock)" },
+       { SCP_ENOENT,           "Configuration file not found" },
+       { SCP_BADCF,            "Configuration file corrupt" },
+       { SCP_NOKEY,            "No such key" },
+       { SCP_NOLINK,           "No such link" },
+       { SCP_EXISTS,           "Key already defined" },
+       { SCP_STALE,            "Write attempted on stale version of object" },
+       { SCP_INVALIDARGUMENT,  "Invalid argument" },
+       { SCP_FAILED,           "Failed!" }
+};
+#define nSCP_ERRMSGS (sizeof(scp_errmsgs)/sizeof(struct scp_errmsg))
+
+
+__private_extern__ CFDataRef
+_SCPSignatureFromStatbuf(const struct stat *statBuf)
+{
+       CFMutableDataRef        signature;
+       SCPSignatureDataRef     sig;
+
+       signature = CFDataCreateMutable(NULL, sizeof(SCPSignatureData));
+       CFDataSetLength(signature, sizeof(SCPSignatureData));
+       sig = (SCPSignatureDataRef)CFDataGetBytePtr(signature);
+       sig->st_dev       = statBuf->st_dev;
+       sig->st_ino       = statBuf->st_ino;
+       sig->st_mtimespec = statBuf->st_mtimespec;
+       sig->st_size      = statBuf->st_size;
+       return signature;
+}
+
+
+__private_extern__ char *
+_SCPPrefsPath(CFStringRef prefsID, boolean_t perUser, CFStringRef user)
+{
+       CFStringRef     path            = NULL;
+       int             pathLen;
+       char            *pathStr;
+
+       if (perUser) {
+               if (prefsID == NULL) {
+                       /* no user prefsID specified */
+                       return NULL;
+               } else if (CFStringHasPrefix(prefsID, CFSTR("/"))) {
+                       /* if absolute path */
+                       path = CFRetain(prefsID);
+               } else {
+                       /*
+                        * relative (to the user's preferences) path
+                        */
+                       char            login[MAXLOGNAME+1];
+                       struct passwd   *pwd;
+
+                       bzero(&login, sizeof(login));
+                       if (user == NULL) {
+                               /* get current console user */
+                               if (SCDConsoleUserGet(login,
+                                                     MAXLOGNAME,
+                                                     NULL,
+                                                     NULL) != SCD_OK) {
+                                       /* if could not get console user */
+                                       return NULL;
+                               }
+                       } else {
+                               /* use specified user */
+                               (void)CFStringGetBytes(user,
+                                                      CFRangeMake(0, CFStringGetLength(user)),
+                                                      kCFStringEncodingMacRoman,
+                                                      0,
+                                                      FALSE,
+                                                      login,
+                                                      MAXLOGNAME,
+                                                      NULL);
+                       }
+
+                       /* get password entry for user */
+                       pwd = getpwnam(login);
+                       if (pwd == NULL) {
+                               /* if no home directory */
+                               return NULL;
+                       }
+
+                       /* create prefs ID */
+                       path = CFStringCreateWithFormat(NULL,
+                                                       NULL,
+                                                       CFSTR("%s/%@/%@"),
+                                                       pwd->pw_dir,
+                                                       PREFS_DEFAULT_USER_DIR,
+                                                       prefsID);
+               }
+       } else {
+               if (prefsID == NULL) {
+                       /* default preference ID */
+                       path = CFStringCreateWithFormat(NULL,
+                                                       NULL,
+                                                       CFSTR("%@/%@"),
+                                                       PREFS_DEFAULT_DIR,
+                                                       PREFS_DEFAULT_CONFIG);
+               } else if (CFStringHasPrefix(prefsID, CFSTR("/"))) {
+                       /* if absolute path */
+                       path = CFRetain(prefsID);
+               } else {
+                       /* relative path */
+                       path = CFStringCreateWithFormat(NULL,
+                                                       NULL,
+                                                       CFSTR("%@/%@"),
+                                                       PREFS_DEFAULT_DIR,
+                                                       prefsID);
+               }
+       }
+
+       /*
+        * convert CFStringRef path to C-string path
+        */
+       pathLen = CFStringGetLength(path) + 1;
+       pathStr = CFAllocatorAllocate(NULL, pathLen, 0);
+       if (!CFStringGetCString(path,
+                               pathStr,
+                               pathLen,
+                               kCFStringEncodingMacRoman)) {
+               SCDLog(LOG_DEBUG, CFSTR("could not convert path to C string"));
+               CFAllocatorDeallocate(NULL, pathStr);
+               pathStr = NULL;
+       }
+
+       CFRelease(path);
+       return pathStr;
+}
+
+
+SCPStatus
+SCPGetSignature(SCPSessionRef session, CFDataRef *signature)
+{
+       SCPSessionPrivateRef    sessionPrivate;
+
+       if (session == NULL) {
+               return SCP_FAILED;           /* you can't do anything with a closed session */
+       }
+       sessionPrivate = (SCPSessionPrivateRef)session;
+
+       *signature = sessionPrivate->signature;
+       return SCP_OK;
+}
+
+
+__private_extern__ CFStringRef
+_SCPNotificationKey(CFStringRef        prefsID,
+                   boolean_t   perUser,
+                   CFStringRef user,
+                   int         keyType)
+{
+       CFStringRef     key             = NULL;
+       char            *pathStr;
+       char            *typeStr;
+
+       pathStr = _SCPPrefsPath(prefsID, perUser, user);
+       if (pathStr == NULL) {
+               return NULL;
+       }
+
+       /* create notification key */
+       switch (keyType) {
+               case kSCPKeyLock :
+                       typeStr = "lock";
+                       break;
+               case kSCPKeyCommit :
+                       typeStr = "commit";
+                       break;
+               case kSCPKeyApply :
+                       typeStr = "apply";
+                       break;
+               default :
+                       typeStr = "?";
+       }
+
+       key = CFStringCreateWithFormat(NULL,
+                                      NULL,
+                                      CFSTR("%@%s:%s"),
+                                      kSCCacheDomainPrefs,
+                                      typeStr,
+                                      pathStr);
+
+       CFAllocatorDeallocate(NULL, pathStr);
+       return key;
+}
+
+
+CFStringRef
+SCPNotificationKeyCreate(CFStringRef prefsID, int keyType)
+{
+       return _SCPNotificationKey(prefsID, FALSE, NULL, keyType);
+}
+
+
+CFStringRef
+SCPUserNotificationKeyCreate(CFStringRef prefsID, CFStringRef user, int keyType)
+{
+       return _SCPNotificationKey(prefsID, TRUE, user, keyType);
+}
+
+
+const char *
+SCPError(SCPStatus status)
+{
+       int i;
+
+       for (i = 0; i < nSCP_ERRMSGS; i++) {
+               if (scp_errmsgs[i].status == status) {
+                       return scp_errmsgs[i].message;
+               }
+       }
+       return "(unknown error)";
+}
diff --git a/SystemConfiguration.fproj/SCP.h b/SystemConfiguration.fproj/SCP.h
new file mode 100644 (file)
index 0000000..3c210c4
--- /dev/null
@@ -0,0 +1,404 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#ifndef _SCP_H
+#define _SCP_H
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <sys/cdefs.h>
+
+/*!
+       @header SCP.h
+       The SystemConfiguration framework provides access to the data used
+               to configure a running system.
+
+       Specifically, the SCPxxx() API's allow an application to load and
+               store XML configuration data in a controlled manner
+               and provides the necessary notifications to other
+               applications which need to be aware of configuration
+               changes.
+
+       The APIs provided by this framework communicate with the "configd"
+               daemon for any tasks requiring synchronization and/or
+               notification.
+ */
+
+
+/*!
+       @enum SCPStatus
+       @discussion Returned status codes.
+       @constant SCP_OK                Success
+       @constant SCP_NOSESSION         Preference session not active
+       @constant SCP_BUSY              Configuration daemon busy
+       @constant SCD_NEEDLOCK          Lock required for this operation
+       @constant SCP_EACCESS           Permission denied (must be root to obtain lock)
+       @constant SCP_ENOENT            Configuration file not found
+       @constant SCP_BADCF             Configuration file corrupt
+       @constant SCD_NOKEY             No such key
+       @constant SCD_NOLINK            No such link
+       @constant SCP_EXISTS            Key already defined
+       @constant SCP_STALE             Write attempted on stale version of object
+       @constant SCP_INVALIDARGUMENT   Invalid argument
+       @constant SCP_FAILED            Generic error
+ */
+typedef enum {
+       SCP_OK                  = 0,    /* Success */
+       SCP_NOSESSION           = 1024, /* Preference session not active */
+       SCP_BUSY                = 1025, /* Preferences update currently in progress */
+       SCP_NEEDLOCK            = 1026, /* Lock required for this operation */
+       SCP_EACCESS             = 1027, /* Permission denied */
+       SCP_ENOENT              = 1028, /* Configuration file not found */
+       SCP_BADCF               = 1029, /* Configuration file corrupt */
+       SCP_NOKEY               = 1030, /* No such key */
+       SCP_NOLINK              = 1031, /* No such link */
+       SCP_EXISTS              = 1032, /* No such key */
+       SCP_STALE               = 1033, /* Write attempted on stale version of object */
+       SCP_INVALIDARGUMENT     = 1034, /* Invalid argument */
+       SCP_FAILED              = 9999  /* Generic error */
+} SCPStatus;
+
+
+/*!
+       @enum SCPOption
+       @discussion Used with the SCP[User]Open() and SCP[User]NotificationKeyCreate()
+               to describe the prefsID CFStringRef argument.
+       @constant kSCPOptionCreatePrefs Specifies that the preferences file should
+               be created if it does not exist.
+ */
+typedef enum {
+       kSCPOpenCreatePrefs     = 1,    /* create preferences file if not found */
+} SCPOption;
+
+
+/*!
+       @enum SCPKeyType
+       @discussion Used with the SCDList() and SCDNotifierAdd() functions to describe
+               the CFStringRef argument.
+       @constant kSCDKeyLock   key used when exclusive access to the stored preferences
+               is obtained or released.
+       @constant kSCDKeyCommit key used when new preferences are committed to the store
+       @constant kSCDKeyApply  key used when new preferences are to be applied to the
+               active system configuration.
+ */
+typedef enum {
+       kSCPKeyLock     = 1,
+       kSCPKeyCommit   = 2,
+       kSCPKeyApply    = 3,
+} SCPKeyType;
+
+
+/*!
+       @typedef SCPSessionRef
+       @discussion This is the type of a handle to an open "session" for
+               accessing system configuration preferences.
+ */
+typedef void *         SCPSessionRef;
+
+
+__BEGIN_DECLS
+
+/*!
+       @function SCPOpen
+       @discussion Initiates access to the per-system set of configuration
+               preferences.
+
+       This function will ensure that the current state of the prefsID is
+       retrieved (by reading the whole thing into memory, or at least,
+       open()'ing the file and keeping it open)
+       @param session A pointer to memory which will be filled with an
+               SCPSessionRef handle to be used for all subsequent requests.
+               If a session cannot be established, the contents of
+               memory pointed to by this parameter are undefined.
+       @param name Pass a string which describes the name of the calling
+               process.
+       @param prefsID Pass a string which identifies the name of the
+               group of preferences to be accessed/updated. A NULL value
+               specifies the default system configuration preferences.
+       @param options Pass a bitfield of type SCPOpenOption containing
+               one or more flags describing how the preferences should
+               be accessed.
+
+       @result A constant of type SCPStatus indicating the success (or
+               failure) of the call.
+ */
+SCPStatus      SCPOpen                         (SCPSessionRef          *session,
+                                                CFStringRef            name,
+                                                CFStringRef            prefsID,
+                                                int                    options);
+
+/*!
+       @function SCPUserOpen
+       @discussion Initiates access to the per-user set of configuration
+               preferences.
+
+       This function will ensure that the current state of the prefsID is
+       retrieved (by reading the whole thing into memory, or at least,
+       open()'ing the file and keeping it open)
+       @param session A pointer to memory which will be filled with an
+               SCPSessionRef handle to be used for all subsequent requests.
+               If a session cannot be established, the contents of
+               memory pointed to by this parameter are undefined.
+       @param name Pass a string which describes the name of the calling
+               process.
+       @param prefsID Pass a string which identifies the name of the
+               group of preferences to be accessed/updated.
+       @param user Pass a string which identifies the user/login who's
+               preferences should be accessed.  A NULL value specifies
+               the current console user.
+       @param options Pass a bitfield of type SCPOpenOption containing
+               one or more flags describing how the preferences should
+               be accessed.
+
+       @result A constant of type SCPStatus indicating the success (or
+               failure) of the call.
+ */
+SCPStatus      SCPUserOpen                     (SCPSessionRef          *session,
+                                                CFStringRef            name,
+                                                CFStringRef            prefsID,
+                                                CFStringRef            user,
+                                                int                    options);
+
+/*!
+       @function SCPClose
+       @discussion Terminates access to a set of configuration preferences.
+
+       This function frees/closes all allocated/opened resources. Any
+       uncommitted changes are NOT written.
+       @param session Pass a pointer to the SCPSessionRef handle which should
+               be closed.
+       @result A constant of type SCPStatus indicating the success (or
+               failure) of the call.
+ */
+SCPStatus      SCPClose                        (SCPSessionRef          *session);
+
+/*!
+       @function SCPLock
+       @discussion Locks access to the configuration preferences.
+
+       This function obtains exclusive access to the configuration
+       preferences associated with this prefsID. Clients attempting
+       to obtain exclusive access the preferences will either receive
+       an SCP_BUSY error or block waiting for the lock to be released.
+       @param session Pass an SCPSessionRef handle which should be used for
+               all API calls.
+       @param wait Pass a boolean flag indicating whether the calling process
+               should block waiting for another process to complete its update
+               operation and release its lock.
+       @result A constant of type SCPStatus indicating the success (or
+               failure) of the call. Possible return values include: SCP_OK,
+               SCP_BUSY, SCP_EACCESS, SCP_STALE.
+ */
+SCPStatus      SCPLock                         (SCPSessionRef          session,
+                                                boolean_t              wait);
+
+/*!
+       @function SCPCommit
+       @discussion Commits changes made to the configuration preferences to
+               persitent storage.
+
+       This function commits the any changes to permanent storage. An
+       implicit call to SCPLock/SCPUnlock will be made if exclusive
+       access had not been established.
+       @param session Pass an SCPSessionRef handle which should be used for
+               all API calls.
+       @result A constant of type SCPStatus indicating the success (or
+               failure) of the call. Possible return values include: SCP_OK,
+               SCP_BUSY, SCP_EACCESS, SCP_STALE.
+ */
+SCPStatus      SCPCommit                       (SCPSessionRef          session);
+
+/*!
+       @function SCPApply
+       @discussion Requests that the currently stored configuration
+               preferences be applied to the active configuration.
+       @param session Pass an SCPSessionRef handle which should be used for
+               all API calls.
+       @result A constant of type SCPStatus indicating the success (or
+               failure) of the call. Possible return values include: SCP_OK,
+               SCP_EACCESS.
+ */
+SCPStatus      SCPApply                        (SCPSessionRef          session);
+
+/*!
+       @function SCPUnlock
+       @discussion Releases exclusive access to the configuration preferences.
+
+       This function releases the exclusive access "lock" fr this prefsID.
+       Other clients will be now be able to establish exclusive access to
+       the preferences.
+       @param session Pass an SCPSessionRef handle which should be used for
+               all API calls.
+       @result A constant of type SCPStatus indicating the success (or
+               failure) of the call.
+ */
+SCPStatus      SCPUnlock                       (SCPSessionRef          session);
+
+/*!
+       @function SCPGetSignature
+       @discussion Returns an sequence of bytes which can be used to determine
+               if the saved configuration preferences have changed.
+       @param session Pass an SCPSessionRef handle which should be used for
+               all API calls.
+       @param signature Pass a pointer to a CFDataRef which will be reflect
+               the signature of the configuration preferences at the time
+               of the call to SCPOpen().
+       @result A constant of type SCPStatus indicating the success (or
+               failure) of the call.
+ */
+SCPStatus      SCPGetSignature                 (SCPSessionRef          session,
+                                                CFDataRef              *signature);
+
+/*!
+       @function SCPList
+       @discussion Returns an array of currently defined preference keys.
+       @param session Pass an SCPSessionRef handle which should be used for
+               all API calls.
+       @param keys Pass a pointer to a CFArrayRef which will be set to a new
+               array of currently defined preference keys.
+       @result A constant of type SCPStatus indicating the success (or
+               failure) of the call.
+ */
+SCPStatus      SCPList                         (SCPSessionRef          session,
+                                                CFArrayRef             *keys);
+
+/*!
+       @function SCPGet
+       @discussion Returns the data associated with a preference key.
+
+       This function retrieves data associated with a key for the prefsID.
+       You "could" read stale data and not know it, unless you first call
+       SCPLock().
+       @param session Pass an SCPSessionRef handle which should be used for
+               all API calls.
+       @param key Pass a reference to the preference key to be returned.
+       @param data Pass a pointer to a CFPropertyListRef which will be set to a
+               new object containing the data associated with the
+               configuration preference.
+       @result A constant of type SCPStatus indicating the success (or
+               failure) of the call. Possible return values include: SCP_OK,
+               SCP_NOKEY.
+ */
+SCPStatus      SCPGet                          (SCPSessionRef          session,
+                                                CFStringRef            key,
+                                                CFPropertyListRef      *data);
+
+/*!
+       @function SCPAdd
+       @discussion Adds data for a preference key.
+
+       This function associates new data with the specified key. In order
+       to commit these changes to permanent storage a call must be made to
+       SCDPCommit().
+       @param session Pass the SCPSessionRef handle which should be used to
+               communicate with the APIs.
+       @param key Pass a reference to the preference key to be updated.
+       @param data Pass a reference to the CFPropertyListRef object containing the
+               data to be associated with the configuration preference.
+       @result A constant of type SCPStatus indicating the success (or
+               failure) of the call. Possible return values include: SCP_OK,
+               SCP_EXISTS.
+ */
+SCPStatus      SCPAdd                          (SCPSessionRef          session,
+                                                CFStringRef            key,
+                                                CFPropertyListRef      data);
+
+/*!
+       @function SCPSet
+       @discussion Updates the data associated with a preference key.
+
+       This function creates (or updates) the data associated with the
+       specified key. In order to commit these changes to permanent
+       storage a call must be made to SCDPCommit().
+       @param session Pass the SCPSessionRef handle which should be used to
+               communicate with the APIs.
+       @param key Pass a reference to the preference key to be updated.
+       @param data Pass a reference to the CFPropertyListRef object containing the
+               data to be associated with the configuration preference.
+       @result A constant of type SCPStatus indicating the success (or
+               failure) of the call. Possible return values include: SCP_OK.
+ */
+SCPStatus      SCPSet                          (SCPSessionRef          session,
+                                                CFStringRef            key,
+                                                CFPropertyListRef      data);
+
+/*!
+       @function SCPRemove
+       @discussion Removes the data associated with a preference key.
+
+       This function removes the data associated with the specified
+       key. In order to commit these changes to permanent storage a
+       call must be made to SCDPCommit().
+       @param session Pass the SCPSessionRef handle which should be used to
+               communicate with the APIs.
+       @param key Pass a reference to the preference key to be removed.
+       @result A constant of type SCPStatus indicating the success (or
+               failure) of the call. Possible return values include: SCP_OK,
+               SCP_NOKEY.
+ */
+SCPStatus      SCPRemove                       (SCPSessionRef          session,
+                                                CFStringRef            key);
+
+/*!
+       @function SCPNotificationKeyCreate
+       @discussion Creates a key which can be used by the SCDNotifierAdd()
+               function to receive notifications of changes to the saved
+               preferences.
+       @param prefsID Pass a string which identifies the name of the
+               preferences to be accessed/updated. A NULL value specifies
+               the default system configuration preferences.
+       @param keyType Pass a kSCPKeyType indicating the type a notification
+               key to be returned.
+       @result A notification string for the specified preference identifier.
+ */
+CFStringRef    SCPNotificationKeyCreate        (CFStringRef            prefsID,
+                                                int                    keyType);
+
+/*!
+       @function SCPUserNotificationKeyCreate
+       @discussion Creates a key which can be used by the SCDNotifierAdd()
+               function to receive notifications of changes to the saved
+               preferences.
+       @param prefsID Pass a string which identifies the name of the
+               preferences to be accessed/updated. A NULL value specifies
+               the default system configuration preferences.
+       @param user Pass a string which identifies the user/login who's
+               preferences should be accessed.  A NULL value specifies
+               the current console user.
+       @param keyType Pass a kSCPKeyType indicating the type a notification
+               key to be returned.
+       @result A notification string for the specified preference identifier.
+ */
+CFStringRef    SCPUserNotificationKeyCreate    (CFStringRef            prefsID,
+                                                CFStringRef            user,
+                                                int                    keyType);
+
+/*!
+       @function SCPError
+       @discussion
+       @param status
+       @result
+ */
+const char *   SCPError                        (SCPStatus              status);
+
+__END_DECLS
+
+#endif /* _SCP_H */
diff --git a/SystemConfiguration.fproj/SCPAdd.c b/SystemConfiguration.fproj/SCPAdd.c
new file mode 100644 (file)
index 0000000..eaba463
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright(c) 2000 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@
+ */
+
+#include <SystemConfiguration/SCP.h>
+#include "SCPPrivate.h"
+
+#include <SystemConfiguration/SCD.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/errno.h>
+
+
+SCPStatus
+SCPAdd(SCPSessionRef session, CFStringRef key, CFPropertyListRef data)
+{
+       SCPSessionPrivateRef    sessionPrivate;
+
+       if (session == NULL) {
+               return SCP_FAILED;           /* you can't do anything with a closed session */
+       }
+       sessionPrivate = (SCPSessionPrivateRef)session;
+
+       if (CFDictionaryContainsKey(sessionPrivate->prefs, key)) {
+               return SCP_EXISTS;
+       }
+
+       CFDictionaryAddValue(sessionPrivate->prefs, key, data);
+       sessionPrivate->changed = TRUE;
+       return SCP_OK;
+}
diff --git a/SystemConfiguration.fproj/SCPApply.c b/SystemConfiguration.fproj/SCPApply.c
new file mode 100644 (file)
index 0000000..8c42762
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright(c) 2000 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@
+ */
+
+#include <SystemConfiguration/SCP.h>
+#include "SCPPrivate.h"
+
+#include <SystemConfiguration/SCD.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/errno.h>
+
+
+SCPStatus
+SCPApply(SCPSessionRef session)
+{
+       SCPSessionPrivateRef    sessionPrivate;
+       SCPStatus               scp_status = SCP_OK;
+       SCDStatus               scd_status;
+       boolean_t               wasLocked;
+
+       if (session == NULL) {
+               return SCP_FAILED;           /* you can't do anything with a closed session */
+       }
+       sessionPrivate = (SCPSessionPrivateRef)session;
+
+       /*
+        * Determine if the we have exclusive access to the preferences
+        * and acquire the lock if necessary.
+        */
+       wasLocked = sessionPrivate->locked;
+       if (!wasLocked) {
+               scp_status = SCPLock(session, TRUE);
+               if (scp_status != SCD_OK) {
+                       SCDLog(LOG_DEBUG, CFSTR("  SCPLock(): %s"), SCPError(scp_status));
+                       return scp_status;
+               }
+       }
+
+       if (!sessionPrivate->isRoot) {
+               /* CONFIGD REALLY NEEDS NON-ROOT WRITE ACCESS */
+               goto notRoot;
+       }
+
+       /* if necessary, create the session "apply" key */
+       if (sessionPrivate->sessionKeyApply == NULL) {
+               sessionPrivate->sessionKeyApply = _SCPNotificationKey(sessionPrivate->prefsID,
+                                                                     sessionPrivate->perUser,
+                                                                     sessionPrivate->user,
+                                                                     kSCPKeyApply);
+       }
+
+       /* post notification */
+       scd_status = SCDLock(sessionPrivate->session);
+       if (scd_status == SCD_OK) {
+               (void) SCDTouch (sessionPrivate->session, sessionPrivate->sessionKeyApply);
+               (void) SCDRemove(sessionPrivate->session, sessionPrivate->sessionKeyApply);
+               (void) SCDUnlock(sessionPrivate->session);
+       } else {
+               SCDLog(LOG_DEBUG, CFSTR("  SCDLock(): %s"), SCDError(scd_status));
+               scp_status = SCP_FAILED;
+       }
+
+    notRoot:
+
+       if (!wasLocked)
+               (void) SCPUnlock(session);
+
+       return scp_status;
+}
diff --git a/SystemConfiguration.fproj/SCPClose.c b/SystemConfiguration.fproj/SCPClose.c
new file mode 100644 (file)
index 0000000..8ad50cf
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright(c) 2000 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@
+ */
+
+#include <SystemConfiguration/SCP.h>
+#include "SCPPrivate.h"
+
+#include <SystemConfiguration/SCD.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/errno.h>
+
+
+SCPStatus
+SCPClose(SCPSessionRef *session)
+{
+       SCPSessionPrivateRef    sessionPrivate;
+
+       if ((session == NULL) || (*session == NULL)) {
+               return SCP_FAILED;           /* you can't do anything with a closed session */
+       }
+       sessionPrivate = (SCPSessionPrivateRef)*session;
+
+       /* release resources */
+       if (sessionPrivate->name)               CFRelease(sessionPrivate->name);
+       if (sessionPrivate->prefsID)            CFRelease(sessionPrivate->prefsID);
+       if (sessionPrivate->user)               CFRelease(sessionPrivate->user);
+       if (sessionPrivate->path)               CFAllocatorDeallocate(NULL, sessionPrivate->path);
+       if (sessionPrivate->signature)          CFRelease(sessionPrivate->signature);
+       if (sessionPrivate->session) {
+               SCDStatus       scd_status;
+
+               scd_status = SCDClose(&sessionPrivate->session);
+               if (scd_status != SCD_OK) {
+                       SCDLog(LOG_INFO, CFSTR("SCDClose() failed: %s"), SCDError(scd_status));
+               }
+       }
+       if (sessionPrivate->sessionKeyLock)     CFRelease(sessionPrivate->sessionKeyLock);
+       if (sessionPrivate->sessionKeyCommit)   CFRelease(sessionPrivate->sessionKeyCommit);
+       if (sessionPrivate->sessionKeyApply)    CFRelease(sessionPrivate->sessionKeyApply);
+       if (sessionPrivate->prefs)              CFRelease(sessionPrivate->prefs);
+
+       /* release session */
+       CFAllocatorDeallocate(NULL, sessionPrivate);
+       *session = NULL;
+       return SCP_OK;
+}
diff --git a/SystemConfiguration.fproj/SCPCommit.c b/SystemConfiguration.fproj/SCPCommit.c
new file mode 100644 (file)
index 0000000..7ea5a5e
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * Copyright(c) 2000 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@
+ */
+
+#include <SystemConfiguration/SCP.h>
+#include "SCPPrivate.h"
+
+#include <SystemConfiguration/SCD.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/errno.h>
+
+
+SCPStatus
+SCPCommit(SCPSessionRef session)
+{
+       SCPSessionPrivateRef    sessionPrivate;
+       SCPStatus               scp_status = SCP_OK;
+       SCDStatus               scd_status;
+       boolean_t               wasLocked;
+
+       if (session == NULL) {
+               return SCP_FAILED;           /* you can't do anything with a closed session */
+       }
+       sessionPrivate = (SCPSessionPrivateRef)session;
+
+       /*
+        * Determine if the we have exclusive access to the preferences
+        * and acquire the lock if necessary.
+        */
+       wasLocked = sessionPrivate->locked;
+       if (!wasLocked) {
+               scp_status = SCPLock(session, TRUE);
+               if (scp_status != SCD_OK) {
+                       SCDLog(LOG_DEBUG, CFSTR("  SCPLock(): %s"), SCPError(scp_status));
+                       return scp_status;
+               }
+       }
+
+       /*
+        * if necessary, apply changes
+        */
+       if (sessionPrivate->changed) {
+               struct stat     statBuf;
+               int             pathLen;
+               char            *newPath;
+               int             fd;
+               CFDataRef       newPrefs;
+
+               if (stat(sessionPrivate->path, &statBuf) == -1) {
+                       if (errno == ENOENT) {
+                               bzero(&statBuf, sizeof(statBuf));
+                               statBuf.st_mode = 0644;
+                               statBuf.st_uid  = geteuid();
+                               statBuf.st_gid  = getegid();
+                       } else {
+                               SCDLog(LOG_DEBUG, CFSTR("stat() failed: %s"), strerror(errno));
+                               scp_status = SCP_FAILED;
+                               goto done;
+                       }
+               }
+
+               /* create the (new) preferences file */
+               pathLen = strlen(sessionPrivate->path) + sizeof("-new");
+               newPath = CFAllocatorAllocate(NULL, pathLen, 0);
+               snprintf(newPath, pathLen, "%s-new", sessionPrivate->path);
+
+               /* open the (new) preferences file */
+           reopen :
+               fd = open(newPath, O_WRONLY|O_CREAT, statBuf.st_mode);
+               if (fd == -1) {
+                       if ((errno == ENOENT) &&
+                           ((sessionPrivate->prefsID == NULL) || !CFStringHasPrefix(sessionPrivate->prefsID, CFSTR("/")))) {
+                               char    *ch;
+
+                               ch = strrchr(newPath, '/');
+                               if (ch != NULL) {
+                                       int     status;
+
+                                       *ch = '\0';
+                                       status = mkdir(newPath, 0755);
+                                       *ch = '/';
+                                       if (status == 0) {
+                                               goto reopen;
+                                       }
+                               }
+                       }
+                       SCDLog(LOG_DEBUG, CFSTR("SCPCommit open() failed: %s"), strerror(errno));
+                       CFAllocatorDeallocate(NULL, newPath);
+                       scp_status = SCP_FAILED;
+                       goto done;
+               }
+
+               /* preserve permissions */
+               (void)fchown(fd, statBuf.st_uid, statBuf.st_gid);
+
+               /* write the new preferences */
+               newPrefs = CFPropertyListCreateXMLData(NULL, sessionPrivate->prefs);
+               (void) write(fd, CFDataGetBytePtr(newPrefs), CFDataGetLength(newPrefs));
+               (void) close(fd);
+               CFRelease(newPrefs);
+
+               /* rename new->old */
+               if (rename(newPath, sessionPrivate->path) == -1) {
+                       SCDLog(LOG_DEBUG, CFSTR("rename() failed: %s"), strerror(errno));
+                       CFAllocatorDeallocate(NULL, newPath);
+                       scp_status = SCP_FAILED;
+                       goto done;
+               }
+               CFAllocatorDeallocate(NULL, newPath);
+
+               /* update signature */
+               if (stat(sessionPrivate->path, &statBuf) == -1) {
+                       SCDLog(LOG_DEBUG, CFSTR("stat() failed: %s"), strerror(errno));
+                       scp_status = SCP_FAILED;
+                       goto done;
+               }
+               CFRelease(sessionPrivate->signature);
+               sessionPrivate->signature = _SCPSignatureFromStatbuf(&statBuf);
+       }
+
+       if (!sessionPrivate->isRoot) {
+               /* CONFIGD REALLY NEEDS NON-ROOT WRITE ACCESS */
+               goto done;
+       }
+
+       /* if necessary, create the session "commit" key */
+       if (sessionPrivate->sessionKeyCommit == NULL) {
+               sessionPrivate->sessionKeyCommit = _SCPNotificationKey(sessionPrivate->prefsID,
+                                                                      sessionPrivate->perUser,
+                                                                      sessionPrivate->user,
+                                                                      kSCPKeyCommit);
+       }
+
+       /* post notification */
+       scd_status = SCDLock(sessionPrivate->session);
+       if (scd_status == SCD_OK) {
+               (void) SCDTouch (sessionPrivate->session, sessionPrivate->sessionKeyCommit);
+               (void) SCDRemove(sessionPrivate->session, sessionPrivate->sessionKeyCommit);
+               (void) SCDUnlock(sessionPrivate->session);
+       } else {
+               SCDLog(LOG_DEBUG, CFSTR("  SCDLock(): %s"), SCDError(scd_status));
+               scp_status = SCP_FAILED;
+       }
+
+    done :
+
+       if (!wasLocked)
+               (void) SCPUnlock(session);
+
+       sessionPrivate->changed = FALSE;
+
+       return scp_status;
+}
diff --git a/SystemConfiguration.fproj/SCPGet.c b/SystemConfiguration.fproj/SCPGet.c
new file mode 100644 (file)
index 0000000..bd0a50a
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright(c) 2000 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@
+ */
+
+#include <SystemConfiguration/SCP.h>
+#include "SCPPrivate.h"
+
+#include <SystemConfiguration/SCD.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/errno.h>
+
+
+SCPStatus
+SCPGet(SCPSessionRef session, CFStringRef key, CFPropertyListRef *data)
+{
+       SCPSessionPrivateRef    sessionPrivate;
+       CFPropertyListRef       val;
+
+       if (session == NULL) {
+               return SCP_FAILED;           /* you can't do anything with a closed session */
+       }
+       sessionPrivate = (SCPSessionPrivateRef)session;
+
+       val = CFDictionaryGetValue(sessionPrivate->prefs, key);
+       if (val == NULL) {
+               return SCP_NOKEY;
+       }
+
+       *data = val;
+       return SCP_OK;
+}
diff --git a/SystemConfiguration.fproj/SCPList.c b/SystemConfiguration.fproj/SCPList.c
new file mode 100644 (file)
index 0000000..3fb1a89
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright(c) 2000 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@
+ */
+
+#include <SystemConfiguration/SCP.h>
+#include "SCPPrivate.h"
+
+#include <SystemConfiguration/SCD.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/errno.h>
+
+
+static CFComparisonResult
+sort_keys(const void *p1, const void *p2, void *context) {
+       CFStringRef key1 = (CFStringRef)p1;
+       CFStringRef key2 = (CFStringRef)p2;
+       return CFStringCompare(key1, key2, 0);
+}
+
+
+SCPStatus
+SCPList(SCPSessionRef session, CFArrayRef *keys)
+{
+       SCPSessionPrivateRef    sessionPrivate;
+       CFIndex                 prefsCnt;
+       void                    **prefsKeys;
+       CFArrayRef              allKeys;
+       CFMutableArrayRef       sortedKeys;
+
+       if (session == NULL) {
+               return SCP_FAILED;           /* you can't do anything with a closed session */
+       }
+       sessionPrivate = (SCPSessionPrivateRef)session;
+
+       prefsCnt  = CFDictionaryGetCount(sessionPrivate->prefs);
+       prefsKeys = CFAllocatorAllocate(NULL, prefsCnt * sizeof(CFStringRef), 0);
+       CFDictionaryGetKeysAndValues(sessionPrivate->prefs, prefsKeys, NULL);
+       allKeys = CFArrayCreate(NULL, prefsKeys, prefsCnt, &kCFTypeArrayCallBacks);
+       CFAllocatorDeallocate(NULL, prefsKeys);
+
+       sortedKeys = CFArrayCreateMutableCopy(NULL, prefsCnt, allKeys);
+       CFRelease(allKeys);
+       CFArraySortValues(sortedKeys,
+                         CFRangeMake(0, prefsCnt),
+                         sort_keys,
+                         NULL);
+
+       *keys = sortedKeys;
+       return SCP_OK;
+}
diff --git a/SystemConfiguration.fproj/SCPLock.c b/SystemConfiguration.fproj/SCPLock.c
new file mode 100644 (file)
index 0000000..760de8d
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * Copyright(c) 2000 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@
+ */
+
+#include <SystemConfiguration/SCP.h>
+#include "SCPPrivate.h"
+
+#include <SystemConfiguration/SCD.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/errno.h>
+
+
+SCPStatus
+SCPLock(SCPSessionRef session, boolean_t wait)
+{
+       SCPStatus               scp_status;
+       SCDStatus               scd_status;
+       SCPSessionPrivateRef    sessionPrivate;
+       SCDHandleRef            handle = NULL;
+       CFDateRef               value;
+       CFArrayRef              changes;
+       struct stat             statBuf;
+       CFDataRef               currentSignature;
+
+       if (session == NULL) {
+               return SCP_FAILED;           /* you can't do anything with a closed session */
+       }
+       sessionPrivate = (SCPSessionPrivateRef)session;
+
+       if (!sessionPrivate->isRoot) {
+               /* CONFIGD REALLY NEEDS NON-ROOT WRITE ACCESS */
+               goto notRoot;
+       }
+
+       if (sessionPrivate->session == NULL) {
+               /* open a session */
+               scd_status = SCDOpen(&sessionPrivate->session, sessionPrivate->name);
+               if (scd_status != SCD_OK) {
+                       SCDLog(LOG_INFO, CFSTR("SCDOpen() failed: %s"), SCDError(scd_status));
+                       return SCP_FAILED;
+               }
+       }
+
+       if (sessionPrivate->sessionKeyLock == NULL) {
+               /* create the session "lock" key */
+               sessionPrivate->sessionKeyLock = _SCPNotificationKey(sessionPrivate->prefsID,
+                                                                    sessionPrivate->perUser,
+                                                                    sessionPrivate->user,
+                                                                    kSCPKeyLock);
+       }
+
+       scd_status = SCDNotifierAdd(sessionPrivate->session,
+                                   sessionPrivate->sessionKeyLock,
+                                   0);
+       if (scd_status != SCD_OK) {
+               SCDLog(LOG_INFO, CFSTR("SCDNotifierAdd() failed: %s"), SCDError(scd_status));
+               scp_status = SCP_FAILED;
+               goto error;
+       }
+
+       handle = SCDHandleInit();
+       value  = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent());
+       SCDHandleSetData(handle, value);
+       CFRelease(value);
+
+       while (TRUE) {
+               /*
+                * Attempt to acquire the lock
+                */
+               scd_status = SCDAddSession(sessionPrivate->session,
+                                          sessionPrivate->sessionKeyLock,
+                                          handle);
+               switch (scd_status) {
+                       case SCD_OK :
+                               scp_status = SCP_OK;
+                               goto done;
+                       case SCD_EXISTS :
+                               if (!wait) {
+                                       scp_status = SCP_BUSY;
+                                       goto error;
+                               }
+                               break;
+                       default :
+                               SCDLog(LOG_INFO, CFSTR("SCDAddSession() failed: %s"), SCDError(scd_status));
+                               scp_status = SCP_FAILED;
+                               goto error;
+               }
+
+               /*
+                * Wait for the lock to be released
+                */
+               scd_status = SCDNotifierWait(sessionPrivate->session);
+               if (scd_status != SCD_OK) {
+                       SCDLog(LOG_INFO, CFSTR("SCDAddSession() failed: %s"), SCDError(scd_status));
+                       scp_status = SCP_FAILED;
+                       goto error;
+               }
+       }
+
+    done :
+
+       SCDHandleRelease(handle);
+       handle = NULL;
+
+       scd_status = SCDNotifierRemove(sessionPrivate->session,
+                                      sessionPrivate->sessionKeyLock,
+                                      0);
+       if (scd_status != SCD_OK) {
+               SCDLog(LOG_INFO, CFSTR("SCDNotifierRemove() failed: %s"), SCDError(scd_status));
+               scp_status = SCP_FAILED;
+               goto error;
+       }
+
+       scd_status = SCDNotifierGetChanges(sessionPrivate->session, &changes);
+       if (scd_status != SCD_OK) {
+               SCDLog(LOG_INFO, CFSTR("SCDNotifierGetChanges() failed: %s"), SCDError(scd_status));
+               scp_status = SCP_FAILED;
+               goto error;
+       }
+       CFRelease(changes);
+
+    notRoot:
+
+       /*
+        * Check the signature
+        */
+       if (stat(sessionPrivate->path, &statBuf) == -1) {
+               if (errno == ENOENT) {
+                       bzero(&statBuf, sizeof(statBuf));
+               } else {
+                       SCDLog(LOG_DEBUG, CFSTR("stat() failed: %s"), strerror(errno));
+                       scp_status = SCP_STALE;
+                       goto error;
+               }
+       }
+
+       currentSignature = _SCPSignatureFromStatbuf(&statBuf);
+       if (!CFEqual(sessionPrivate->signature, currentSignature)) {
+               CFRelease(currentSignature);
+               scp_status = SCP_STALE;
+               goto error;
+       }
+       CFRelease(currentSignature);
+
+       sessionPrivate->locked = TRUE;
+       return SCD_OK;
+
+    error :
+
+       if (handle)     SCDHandleRelease(handle);
+       return scp_status;
+}
diff --git a/SystemConfiguration.fproj/SCPOpen.c b/SystemConfiguration.fproj/SCPOpen.c
new file mode 100644 (file)
index 0000000..dd3e772
--- /dev/null
@@ -0,0 +1,223 @@
+/*
+ * Copyright(c) 2000 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@
+ */
+
+#include <SystemConfiguration/SCP.h>
+#include <SystemConfiguration/SCPPath.h>
+#include "SCPPrivate.h"
+
+#include <SystemConfiguration/SCD.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/errno.h>
+
+
+static SCPStatus
+_SCPOpen(SCPSessionRef *session,
+        CFStringRef    name,
+        CFStringRef    prefsID,
+        boolean_t      perUser,
+        CFStringRef    user,
+        int            options)
+{
+       SCPStatus               scp_status;
+       SCPSessionPrivateRef    newSession;
+       int                     fd              = -1;
+       struct stat             statBuf;
+       CFMutableDataRef        xmlData;
+       CFStringRef             xmlError;
+
+       newSession = (SCPSessionPrivateRef)CFAllocatorAllocate(NULL, sizeof(SCPSessionPrivate), 0);
+       newSession->name                = NULL;
+       newSession->prefsID             = NULL;
+       newSession->perUser             = perUser;
+       newSession->user                = NULL;
+       newSession->path                = NULL;
+       newSession->signature           = NULL;
+       newSession->session             = NULL;
+       newSession->sessionKeyLock      = NULL;
+       newSession->sessionKeyCommit    = NULL;
+       newSession->sessionKeyApply     = NULL;
+       newSession->prefs               = NULL;
+       newSession->changed             = FALSE;
+       newSession->locked              = FALSE;
+       newSession->isRoot              = (geteuid() == 0);
+
+       /*
+        * convert prefsID to path
+        */
+       newSession->path = _SCPPrefsPath(prefsID, perUser, user);
+       if (newSession->path == NULL) {
+               scp_status = SCP_FAILED;
+               goto error;
+       }
+
+       /*
+        * open file
+        */
+       fd = open(newSession->path, O_RDONLY, 0644);
+       if (fd == -1) {
+               char    *errmsg = strerror(errno);
+
+               switch (errno) {
+                       case ENOENT :
+                               if (options & kSCPOpenCreatePrefs) {
+                                       bzero(&statBuf, sizeof(statBuf));
+                                       goto create_1;
+                               }
+                               scp_status = SCP_ENOENT;
+                               break;
+                       case EACCES :
+                               scp_status = SCP_EACCESS;
+                               break;
+                       default :
+                               scp_status = SCP_FAILED;
+               }
+               SCDLog(LOG_DEBUG, CFSTR("open() failed: %s"), errmsg);
+               goto error;
+       }
+
+       /*
+        * check file, create signature
+        */
+       if (fstat(fd, &statBuf) == -1) {
+               SCDLog(LOG_DEBUG, CFSTR("fstat() failed: %s"), strerror(errno));
+               scp_status = SCP_FAILED;
+               goto error;
+       }
+
+    create_1 :
+
+       newSession->signature = _SCPSignatureFromStatbuf(&statBuf);
+
+       if (statBuf.st_size > 0) {
+               /*
+                * extract property list
+                */
+               xmlData = CFDataCreateMutable(NULL, statBuf.st_size);
+               CFDataSetLength(xmlData, statBuf.st_size);
+               if (read(fd, (void *)CFDataGetBytePtr(xmlData), statBuf.st_size) != statBuf.st_size) {
+                       SCDLog(LOG_DEBUG, CFSTR("_SCPOpen read(): could not load preference data."));
+                       CFRelease(xmlData);
+                       xmlData = NULL;
+                       if (options & kSCPOpenCreatePrefs) {
+                               goto create_2;
+                       }
+                       scp_status = SCP_BADCF;
+                       goto error;
+               }
+
+               /*
+                * load preferences
+                */
+               newSession->prefs = (CFMutableDictionaryRef)
+                                   CFPropertyListCreateFromXMLData(NULL,
+                                                                   xmlData,
+                                                                   kCFPropertyListMutableContainers,
+                                                                   &xmlError);
+               CFRelease(xmlData);
+               if (xmlError) {
+                       SCDLog(LOG_DEBUG, CFSTR("_SCPOpen CFPropertyListCreateFromXMLData(): %s"), xmlError);
+                       if (options & kSCPOpenCreatePrefs) {
+                               goto create_2;
+                       }
+                       scp_status = SCP_BADCF;
+                       goto error;
+               }
+
+               /*
+                * make sure that we've got a dictionary
+                */
+               if (CFGetTypeID(newSession->prefs) != CFDictionaryGetTypeID()) {
+                               SCDLog(LOG_DEBUG, CFSTR("_SCPOpen CFGetTypeID(): not a dictionary."));
+                               CFRelease(newSession->prefs);
+                               newSession->prefs = NULL;
+                               if (options & kSCPOpenCreatePrefs) {
+                                       goto create_2;
+                               }
+                       scp_status = SCP_BADCF;
+                       goto error;
+               }
+       }
+
+    create_2 :
+
+       if (fd != -1) {
+               (void) close(fd);
+               fd = -1;
+       }
+
+       if (newSession->prefs == NULL) {
+               /*
+                * new file, create empty preferences
+                */
+               SCDLog(LOG_DEBUG, CFSTR("_SCPOpen(): creating new dictionary."));
+               newSession->prefs = CFDictionaryCreateMutable(NULL,
+                                                             0,
+                                                             &kCFTypeDictionaryKeyCallBacks,
+                                                             &kCFTypeDictionaryValueCallBacks);
+               newSession->changed = TRUE;
+       }
+
+       /*
+        * all OK
+        */
+       newSession->name                = CFRetain(name);
+       if (prefsID) {
+               newSession->prefsID     = CFRetain(prefsID);
+       }
+       newSession->perUser             = perUser;
+       if (user) {
+               newSession->user        = CFRetain(user);
+       }
+       *session   = (SCPSessionRef)newSession;
+       return SCP_OK;
+
+    error :
+
+       if (fd != -1) {
+               (void)close(fd);
+       }
+       (void) SCPClose((SCPSessionRef *)&newSession);
+       return scp_status;
+}
+
+
+SCPStatus
+SCPOpen(SCPSessionRef  *session,
+       CFStringRef     name,
+       CFStringRef     prefsID,
+       int             options)
+{
+       return _SCPOpen(session, name, prefsID, FALSE, NULL, options);
+}
+
+
+SCPStatus
+SCPUserOpen(SCPSessionRef      *session,
+           CFStringRef         name,
+           CFStringRef         prefsID,
+           CFStringRef         user,
+           int                 options)
+{
+       return _SCPOpen(session, name, prefsID, TRUE, user, options);
+}
diff --git a/SystemConfiguration.fproj/SCPPath.c b/SystemConfiguration.fproj/SCPPath.c
new file mode 100644 (file)
index 0000000..aee9cf8
--- /dev/null
@@ -0,0 +1,428 @@
+/*
+ * Copyright(c) 2000 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@
+ */
+
+#include <SystemConfiguration/SCD.h>
+#include <SystemConfiguration/SCP.h>
+#include "SCPPrivate.h"
+#include <SystemConfiguration/SCPreferences.h>
+#include <SystemConfiguration/SCPPath.h>
+
+static CFArrayRef
+normalizePath(CFStringRef path)
+{
+       CFArrayRef              tmpElements;
+       CFMutableArrayRef       elements;
+       CFIndex                 nElements;
+       CFIndex                 i;
+
+       if (!CFStringHasPrefix(path, CFSTR("/"))) {
+               /* if no root separator */
+               return NULL;
+       }
+
+       tmpElements = CFStringCreateArrayBySeparatingStrings(NULL, path, CFSTR("/"));
+       elements    = CFArrayCreateMutableCopy(NULL, 0, tmpElements);
+       CFRelease(tmpElements);
+
+       /* remove empty path components */
+       nElements = CFArrayGetCount(elements);
+       for (i=nElements; i>0; i--) {
+               CFStringRef     pathElement;
+
+               pathElement = CFArrayGetValueAtIndex(elements, i-1);
+               if (CFStringGetLength(pathElement) == 0) {
+                       CFArrayRemoveValueAtIndex(elements, i-1);
+                       nElements--;
+               }
+       }
+
+       if (nElements < 1) {
+               CFRelease(elements);
+               return NULL;
+       }
+
+       return elements;
+}
+
+
+static SCPStatus
+getPath(SCPSessionRef session, CFStringRef path, CFMutableDictionaryRef *entity)
+{
+       CFArrayRef              elements;
+       CFIndex                 i;
+       CFIndex                 nElements;
+       SCPStatus               status;
+       CFMutableDictionaryRef  value           = NULL;
+
+       if (session == NULL) {
+               return SCP_NOSESSION;   /* you can't do anything with a closed session */
+       }
+
+       elements = normalizePath(path);
+       if (elements == NULL) {
+               return SCP_NOKEY;
+       }
+
+       /* get preferences key */
+       status = SCPGet(session,
+                       CFArrayGetValueAtIndex(elements, 0),
+                       (CFPropertyListRef *)&value);
+       if (status != SCP_OK) {
+               goto done;
+       }
+
+       if (CFGetTypeID(value) != CFDictionaryGetTypeID()) {
+               status = SCP_NOKEY;
+               goto done;
+       }
+
+       nElements = CFArrayGetCount(elements);
+       for (i=1; i<nElements; i++) {
+               CFStringRef     element;
+
+               element = CFArrayGetValueAtIndex(elements, i);
+               value   = (CFMutableDictionaryRef)CFDictionaryGetValue(value, element);
+               if (value == NULL) {
+                       /* if (parent) path component does not exist */
+                       status = SCP_NOKEY;
+                       goto done;
+               }
+
+               if (CFGetTypeID(value) != CFDictionaryGetTypeID()) {
+                       status = SCP_NOKEY;
+                       goto done;
+               }
+
+       }
+
+       *entity = value;
+       status = SCP_OK;
+
+    done :
+
+       CFRelease(elements);
+       return status;
+}
+
+
+SCPStatus
+SCPPathCreateUniqueChild(SCPSessionRef session,
+                        CFStringRef    prefix,
+                        CFStringRef    *newPath)
+{
+       SCPStatus               status;
+       CFMutableDictionaryRef  value;
+       boolean_t               newValue        = FALSE;
+       CFIndex                 i;
+       CFStringRef             path;
+       CFMutableDictionaryRef  newDict;
+
+       if (session == NULL) {
+               return SCP_NOSESSION;   /* you can't do anything with a closed session */
+       }
+
+       status = getPath(session, prefix, &value);
+       switch (status) {
+               case SCP_OK :
+                       break;
+               case SCP_NOKEY :
+                       value = CFDictionaryCreateMutable(NULL,
+                                                         0,
+                                                         &kCFTypeDictionaryKeyCallBacks,
+                                                         &kCFTypeDictionaryValueCallBacks);
+                       newValue = TRUE;
+                       break;
+               default :
+                       return status;
+       }
+
+       if (CFGetTypeID(value) != CFDictionaryGetTypeID()) {
+               /* if specified path is not a dictionary */
+               status = SCP_NOKEY;
+               goto done;
+       }
+
+       if (CFDictionaryContainsKey(value, kSCResvLink)) {
+               /* the path is a link... */
+               status = SCP_FAILED;
+               goto done;
+       }
+
+       i = 0;
+       while (TRUE) {
+               CFStringRef     pathComponent;
+               Boolean         found;
+
+               pathComponent = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), i);
+               found = CFDictionaryContainsKey(value, pathComponent);
+               CFRelease(pathComponent);
+
+               if (!found) {
+                       /* if we've identified the next unique key */
+                       path = CFStringCreateWithFormat(NULL,
+                                                       NULL,
+                                                       CFSTR("%@/%i"),
+                                                       prefix,
+                                                       i);
+                       break;
+               }
+               i++;
+       }
+
+       /* save the new dictionary */
+       newDict = CFDictionaryCreateMutable(NULL,
+                                           0,
+                                           &kCFTypeDictionaryKeyCallBacks,
+                                           &kCFTypeDictionaryValueCallBacks);
+       status = SCPPathSetValue(session, path, newDict);
+       CFRelease(newDict);
+       if (status != SCP_OK) {
+               CFRelease(path);
+               goto done;
+       }
+
+       *newPath = path;
+
+    done :
+
+       if (newValue)   CFRelease(value);
+       return status;
+}
+
+
+SCPStatus
+SCPPathGetValue(SCPSessionRef  session,
+               CFStringRef     path,
+               CFDictionaryRef *value)
+{
+       SCPStatus               status;
+       CFMutableDictionaryRef  entity;
+       CFStringRef             entityLink;
+
+       if (session == NULL) {
+               return SCP_NOSESSION;   /* you can't do anything with a closed session */
+       }
+
+       status = getPath(session, path, &entity);
+       if (status != SCP_OK) {
+               return status;
+       }
+
+/* XXXX Add code here to chase multiple links XXXXX */
+
+       if ((CFGetTypeID(entity) == CFDictionaryGetTypeID()) &&
+           (CFDictionaryGetValueIfPresent(entity, kSCResvLink, (void **)&entityLink))) {
+                   /* if this is a dictionary AND it is a link */
+                   status = getPath(session, entityLink, &entity);
+                   if (status != SCP_OK) {
+                           /* if it was a bad link */
+                           return status;
+                   }
+       }
+
+       *value = entity;
+       return status;
+}
+
+
+SCPStatus
+SCPPathGetLink(SCPSessionRef           session,
+              CFStringRef              path,
+              CFStringRef              *link)
+{
+       SCPStatus               status;
+       CFMutableDictionaryRef  entity;
+       CFStringRef             entityLink;
+
+       if (session == NULL) {
+               return SCP_NOSESSION;   /* you can't do anything with a closed session */
+       }
+
+       status = getPath(session, path, &entity);
+       if (status != SCP_OK) {
+               return status;
+       }
+
+       if ((CFGetTypeID(entity) == CFDictionaryGetTypeID()) &&
+           (CFDictionaryGetValueIfPresent(entity, kSCResvLink, (void **)&entityLink))) {
+                   /* if this is a dictionary AND it is a link */
+               *link = entityLink;
+               return status;
+       }
+
+       return SCP_NOKEY;
+}
+
+
+SCPStatus
+SCPPathSetValue(SCPSessionRef session, CFStringRef path, CFDictionaryRef value)
+{
+       CFMutableDictionaryRef  element;
+       CFArrayRef              elements        = NULL;
+       CFIndex                 i;
+       CFIndex                 nElements;
+       boolean_t               newRoot         = FALSE;
+       CFMutableDictionaryRef  root            = NULL;
+       SCPStatus               status          = SCP_NOKEY;
+
+       if (session == NULL) {
+               return SCP_NOSESSION;   /* you can't do anything with a closed session */
+       }
+
+       elements = normalizePath(path);
+       if (elements == NULL) {
+               return SCP_NOKEY;
+       }
+
+       /* get preferences key */
+       status = SCPGet(session,
+                       CFArrayGetValueAtIndex(elements, 0),
+                       (CFPropertyListRef *)&root);
+       if (status != SCP_OK) {
+               root = CFDictionaryCreateMutable(NULL,
+                                                 0,
+                                                 &kCFTypeDictionaryKeyCallBacks,
+                                                 &kCFTypeDictionaryValueCallBacks);
+               newRoot = TRUE;
+       }
+
+       nElements = CFArrayGetCount(elements);
+       if (nElements == 1) {
+               /* if we are only updating the data associated with the preference key */
+               if (newRoot) {
+                       CFRelease(root);
+                       newRoot = FALSE;
+               }
+               root = (CFMutableDictionaryRef)value;
+       }
+
+       element = root;
+       for (i=1; i<nElements-1; i++) {
+               CFStringRef             pathComponent;
+               CFMutableDictionaryRef  tmpElement;
+
+               pathComponent = CFArrayGetValueAtIndex(elements, i);
+               tmpElement  = (void *)CFDictionaryGetValue(element, pathComponent);
+               if (tmpElement == NULL) {
+                       /* if (parent) path component does not exist */
+                       tmpElement = CFDictionaryCreateMutable(NULL,
+                                                              0,
+                                                              &kCFTypeDictionaryKeyCallBacks,
+                                                              &kCFTypeDictionaryValueCallBacks);
+                       CFDictionarySetValue(element, pathComponent, tmpElement);
+                       CFRelease(tmpElement);
+               }
+               element = tmpElement;
+       }
+
+       if (nElements > 1) {
+               CFDictionarySetValue(element,
+                                    CFArrayGetValueAtIndex(elements, nElements-1),
+                                    value);
+       }
+       status = SCPSet(session, CFArrayGetValueAtIndex(elements, 0), root);
+
+       if (newRoot)    CFRelease(root);
+       CFRelease(elements);
+       return status;
+}
+
+
+SCPStatus
+SCPPathSetLink(SCPSessionRef session, CFStringRef path, CFStringRef link)
+{
+       CFMutableDictionaryRef  dict;
+       SCPStatus               status;
+
+       if (session == NULL) {
+               return SCP_NOSESSION;   /* you can't do anything with a closed session */
+       }
+
+       dict = CFDictionaryCreateMutable(NULL,
+                                        0,
+                                        &kCFTypeDictionaryKeyCallBacks,
+                                        &kCFTypeDictionaryValueCallBacks);
+       CFDictionaryAddValue(dict, kSCResvLink, link);
+       status = SCPPathSetValue(session, path, dict);
+       CFRelease(dict);
+
+       return status;
+}
+
+
+SCPStatus
+SCPPathRemove(SCPSessionRef session, CFStringRef path)
+{
+       CFMutableDictionaryRef  element;
+       CFArrayRef              elements        = NULL;
+       CFIndex                 i;
+       CFIndex                 nElements;
+       CFMutableDictionaryRef  root            = NULL;
+       SCPStatus               status          = SCP_NOKEY;
+
+       if (session == NULL) {
+               return SCP_NOSESSION;   /* you can't do anything with a closed session */
+       }
+
+       elements = normalizePath(path);
+       if (elements == NULL) {
+               return SCP_NOKEY;
+       }
+
+       /* get preferences key */
+       status = SCPGet(session,
+                       CFArrayGetValueAtIndex(elements, 0),
+                       (CFPropertyListRef *)&root);
+       if (status != SCP_OK) {
+               goto done;
+       }
+
+       nElements = CFArrayGetCount(elements);
+       if (nElements == 1) {
+               /* if we are removing the data associated with the preference key */
+               status = SCPRemove(session, CFArrayGetValueAtIndex(elements, 0));
+               goto done;
+       }
+
+       element = root;
+       for (i=1; i<nElements-1; i++) {
+               CFStringRef             pathComponent;
+               CFMutableDictionaryRef  tmpElement;
+
+               pathComponent = CFArrayGetValueAtIndex(elements, i);
+               tmpElement    = (void *)CFDictionaryGetValue(element, pathComponent);
+               if (tmpElement == NULL) {
+                       status = SCP_NOKEY;
+                       goto done;
+               }
+               element = tmpElement;
+       }
+
+       CFDictionaryRemoveValue(element,
+                               CFArrayGetValueAtIndex(elements, nElements-1));
+       status = SCPSet(session, CFArrayGetValueAtIndex(elements, 0), root);
+
+    done :
+
+       CFRelease(elements);
+       return status;
+}
diff --git a/SystemConfiguration.fproj/SCPPath.h b/SystemConfiguration.fproj/SCPPath.h
new file mode 100644 (file)
index 0000000..f400518
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#ifndef _SCPPATH_H
+#define _SCPPATH_H
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <sys/cdefs.h>
+
+/*!
+       @header SCPPath.h
+       The SystemConfiguration framework provides access to the data used
+               to configure a running system.
+
+       Specifically, the SCPPathXXX() API's allow an application to
+               load and store XML configuration data in a controlled
+               manner and provides the necessary notifications to other
+               applications which need to be aware of configuration
+               changes.
+
+       The SCPPathXXX() API's make certain assumptions about the layout
+               of the preferences data.  These APIs view the data as a
+               collection of dictionaries of key/value pairs and an
+               associated path name.  The root path ("/") identifies
+               the top-level dictionary.  Additional path components
+               specify the keys for sub-dictionaries.
+
+               For example, the following dictionary can be access via
+               two paths.  The root ("/") path would return a property
+               list with all keys and values.  The path "/path1" would
+               only return the dictionary with the "key3" and "key4"
+               properties.
+
+               <dict>
+                       <key>key1</key>
+                       <string>val1</string>
+                       <key>key2</key>
+                       <string>val2</string>
+                       <key>path1</key>
+                       <dict>
+                               <key>key3</key>
+                               <string>val3</string>
+                               <key>key4</key>
+                               <string>val4</string>
+                       </dict>
+               </dict>
+
+       The APIs provided by this framework communicate with the "configd"
+               daemon for any tasks requiring synchronization and/or
+               notification.
+ */
+
+
+__BEGIN_DECLS
+
+/*!
+       @function SCPPathCreateUniqueChild
+       @discussion Creates a new path component within the dictionary
+               hierarchy.
+       @param session Pass the SCPSessionRef handle which should be used to
+        communicate with the APIs.
+       @param prefix Pass a string which represents the parent path.
+       @param newPath A pointer to memory which will be filled with an
+               string representing the new child path.
+       @result A constant of type SCPStatus indicating the success (or
+               failure) of the call. Possible return values include: SCP_OK,
+               SCP_NOKEY.
+ */
+SCPStatus      SCPPathCreateUniqueChild        (SCPSessionRef          session,
+                                                CFStringRef            prefix,
+                                                CFStringRef            *newPath);
+
+/*!
+       @function SCPPathGetValue
+       @discussion Returns the dictionary associated with the specified
+               path.
+       @param session Pass the SCPSessionRef handle which should be used to
+        communicate with the APIs.
+       @param path Pass a string whcih represents the path to be returned.
+       @param value A pointer to memory which will be filled with an
+               dictionary associated with the specified path.
+       @result A constant of type SCPStatus indicating the success (or
+               failure) of the call. Possible return values include: SCP_OK,
+               SCP_NOKEY.
+ */
+SCPStatus      SCPPathGetValue                 (SCPSessionRef          session,
+                                                CFStringRef            path,
+                                                CFDictionaryRef        *value);
+
+/*!
+       @function SCPPathGetLink
+       @discussion Returns the link (if one exists) associatd with the
+               specified path.
+       @param session Pass the SCPSessionRef handle which should be used to
+        communicate with the APIs.
+       @param path Pass a string whcih represents the path to be returned.
+       @param link A pointer to memory which will be filled with a
+               string reflecting the link found at the specified path.
+               If no link was present at the specified path a status
+               value of SCP_NOKEY will be returned.
+       @result A constant of type SCPStatus indicating the success (or
+               failure) of the call. Possible return values include: SCP_OK,
+               SCP_NOKEY.
+ */
+SCPStatus      SCPPathGetLink                  (SCPSessionRef          session,
+                                                CFStringRef            path,
+                                                CFStringRef            *link);
+
+/*!
+       @function SCPPathSetValue
+       @discussion Associates a dictionary with the specified path.
+       @param session Pass the SCPSessionRef handle which should be used to
+        communicate with the APIs.
+       @param path Pass a string whcih represents the path to be returned.
+       @param value Pass a dictionary which represents the data to be
+               stored at the specified path.
+       @result A constant of type SCPStatus indicating the success (or
+               failure) of the call. Possible return values include: SCP_OK.
+ */
+SCPStatus      SCPPathSetValue                 (SCPSessionRef          session,
+                                                CFStringRef            path,
+                                                CFDictionaryRef        value);
+
+/*!
+       @function SCPPathSetLink
+       @discussion Associates a link to a second dictionary at the
+               specified path.
+       @param session Pass the SCPSessionRef handle which should be used to
+        communicate with the APIs.
+       @param path Pass a string whcih represents the path to be returned.
+       @param value Pass a string which represents the path to be stored
+               at the specified path.
+       @result A constant of type SCPStatus indicating the success (or
+               failure) of the call. Possible return values include: SCP_OK,
+               SCP_NOKEY.
+ */
+SCPStatus      SCPPathSetLink                  (SCPSessionRef          session,
+                                                CFStringRef            path,
+                                                CFStringRef            link);
+
+/*!
+       @function SCPPathRemove
+       @discussion Removes the data associated with the specified path.
+       @param session Pass the SCPSessionRef handle which should be used to
+        communicate with the APIs.
+       @param path Pass a string whcih represents the path to be returned.
+       @result A constant of type SCPStatus indicating the success (or
+               failure) of the call. Possible return values include: SCP_OK,
+               SCP_NOKEY.
+ */
+SCPStatus      SCPPathRemove                   (SCPSessionRef          session,
+                                                CFStringRef            path);
+
+__END_DECLS
+
+#endif /* _SCPPATH_H */
diff --git a/SystemConfiguration.fproj/SCPPrivate.h b/SystemConfiguration.fproj/SCPPrivate.h
new file mode 100644 (file)
index 0000000..707279c
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#ifndef _SCPPRIVATE_H
+#define _SCPPRIVATE_H
+
+#include <SystemConfiguration/SCP.h>
+#include <SystemConfiguration/SCD.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+
+#define        PREFS_DEFAULT_DIR       CFSTR("/var/db/SystemConfiguration")
+#define        PREFS_DEFAULT_CONFIG    CFSTR("preferences.xml")
+
+#define        PREFS_DEFAULT_USER_DIR  CFSTR("Library/Preferences")
+
+
+/* Define the per-preference-handle structure */
+typedef struct {
+       /* session name */
+       CFStringRef             name;
+
+       /* preferences ID */
+       CFStringRef             prefsID;
+
+       /* per-user preference info */
+       boolean_t               perUser;
+       CFStringRef             user;
+
+       /* configuration file path */
+       char                    *path;
+
+       /* configuration file signature */
+       CFDataRef               signature;
+
+       /* configd session */
+       SCDSessionRef           session;
+
+       /* configd session keys */
+       CFStringRef             sessionKeyLock;
+       CFStringRef             sessionKeyCommit;
+       CFStringRef             sessionKeyApply;
+
+       /* preferences */
+       CFMutableDictionaryRef  prefs;
+
+       /* flags */
+       boolean_t               changed;
+       boolean_t               locked;
+       boolean_t               isRoot;
+
+} SCPSessionPrivate, *SCPSessionPrivateRef;
+
+
+/* Define signature data */
+typedef struct {
+       dev_t     st_dev;               /* inode's device */
+       ino_t     st_ino;               /* inode's number */
+       struct  timespec st_mtimespec;  /* time of last data modification */
+       off_t     st_size;              /* file size, in bytes */
+} SCPSignatureData, *SCPSignatureDataRef;
+
+
+__BEGIN_DECLS
+
+CFDataRef      _SCPSignatureFromStatbuf        (const struct stat      *statBuf);
+
+char *         _SCPPrefsPath                   (CFStringRef            prefsID,
+                                                boolean_t              perUser,
+                                                CFStringRef            user);
+
+CFStringRef    _SCPNotificationKey             (CFStringRef            prefsID,
+                                                boolean_t              perUser,
+                                                CFStringRef            user,
+                                                int                    keyType);
+
+__END_DECLS
+
+#endif /* _SCPPRIVATE_H */
diff --git a/SystemConfiguration.fproj/SCPRemove.c b/SystemConfiguration.fproj/SCPRemove.c
new file mode 100644 (file)
index 0000000..a861973
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright(c) 2000 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@
+ */
+
+#include <SystemConfiguration/SCP.h>
+#include "SCPPrivate.h"
+
+#include <SystemConfiguration/SCD.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/errno.h>
+
+
+SCPStatus
+SCPRemove(SCPSessionRef session, CFStringRef key)
+{
+       SCPSessionPrivateRef    sessionPrivate;
+
+       if (session == NULL) {
+               return SCP_FAILED;           /* you can't do anything with a closed session */
+       }
+       sessionPrivate = (SCPSessionPrivateRef)session;
+
+       if (!CFDictionaryContainsKey(sessionPrivate->prefs, key)) {
+               return SCP_NOKEY;
+       }
+
+       CFDictionaryRemoveValue(sessionPrivate->prefs, key);
+       sessionPrivate->changed = TRUE;
+       return SCP_OK;
+}
diff --git a/SystemConfiguration.fproj/SCPSet.c b/SystemConfiguration.fproj/SCPSet.c
new file mode 100644 (file)
index 0000000..6b875ae
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright(c) 2000 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@
+ */
+
+#include <SystemConfiguration/SCP.h>
+#include "SCPPrivate.h"
+
+#include <SystemConfiguration/SCD.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/errno.h>
+
+
+SCPStatus
+SCPSet(SCPSessionRef session, CFStringRef key, CFPropertyListRef data)
+{
+       SCPSessionPrivateRef    sessionPrivate;
+
+       if (session == NULL) {
+               return SCP_FAILED;           /* you can't do anything with a closed session */
+       }
+       sessionPrivate = (SCPSessionPrivateRef)session;
+
+       CFDictionarySetValue(sessionPrivate->prefs, key, data);
+       sessionPrivate->changed = TRUE;
+       return SCP_OK;
+}
diff --git a/SystemConfiguration.fproj/SCPUnlock.c b/SystemConfiguration.fproj/SCPUnlock.c
new file mode 100644 (file)
index 0000000..4d808a8
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright(c) 2000 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@
+ */
+
+#include <SystemConfiguration/SCP.h>
+#include "SCPPrivate.h"
+
+#include <SystemConfiguration/SCD.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/errno.h>
+
+
+SCPStatus
+SCPUnlock(SCPSessionRef session)
+{
+       SCPSessionPrivateRef    sessionPrivate;
+       SCDStatus               scd_status;
+
+       if (session == NULL) {
+               return SCP_FAILED;           /* you can't do anything with a closed session */
+       }
+       sessionPrivate = (SCPSessionPrivateRef)session;
+
+       if (!sessionPrivate->locked) {
+               return SCP_NEEDLOCK;    /* sorry, you don't have the lock */
+       }
+
+       if (!sessionPrivate->isRoot) {
+               /* CONFIGD REALLY NEEDS NON-ROOT WRITE ACCESS */
+               goto notRoot;
+       }
+
+       scd_status = SCDRemove(sessionPrivate->session, sessionPrivate->sessionKeyLock);
+       if (scd_status != SCD_OK) {
+               SCDLog(LOG_INFO, CFSTR("SCDRemove() failed: %s"), SCDError(scd_status));
+               return SCP_FAILED;
+       }
+
+    notRoot:
+
+       sessionPrivate->locked = FALSE;
+       return SCP_OK;
+}
diff --git a/SystemConfiguration.fproj/SystemConfiguration.h b/SystemConfiguration.fproj/SystemConfiguration.h
new file mode 100644 (file)
index 0000000..9d4ef22
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#ifndef _SYSTEMCONFIGURATION_H
+#define _SYSTEMCONFIGURATION_H
+
+/*!
+       @header SystemConfiguration.h
+       The SystemConfiguration framework provides access to the data used to configure a running system.  The APIs provided by this framework communicate with the "configd" daemon.
+
+The "configd" daemon manages a "cache" reflecting the desired configuration settings as well as the current state of the system.  The daemon provides a notification mechanism for user-level processes which need to be aware of changes made to the "cache" data.  Lastly, the daemon loads a number of bundles(or plug-ins) which monitor low-level kernel events and, via a set of policy modules, keep this cached data up to date.
+
+The "configd" daemon also provides an address space/task/process which can be used by other CFRunLoop based functions which would otherwise require their own process/daemon for execution.
+
+ */
+
+/* cache access APIs */
+#include <SystemConfiguration/SCD.h>
+#include <SystemConfiguration/SCDKeys.h>
+
+/* preference access APIs */
+#include <SystemConfiguration/SCP.h>
+#include <SystemConfiguration/SCPPath.h>
+#include <SystemConfiguration/SCPreferences.h>
+
+/* "console user" APIs */
+#include <SystemConfiguration/SCDConsoleUser.h>
+
+/* "computer/host name" APIs */
+#include <SystemConfiguration/SCDHostName.h>
+
+/* "network reachability" APIs */
+#include <SystemConfiguration/SCNetwork.h>
+
+#endif /* _SYSTEMCONFIGURATION_H */
diff --git a/SystemConfiguration.fproj/config.defs b/SystemConfiguration.fproj/config.defs
new file mode 100644 (file)
index 0000000..7449dea
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include <mach/std_types.defs>
+#include <mach/mach_types.defs>
+
+subsystem config 20000;
+serverprefix _;
+
+import "config_types.h";
+
+/*
+ * serialized XML data (client->server)
+ */
+type xmlData    = ^ array [] of MACH_MSG_TYPE_BYTE
+       ctype : xmlData_t;
+
+/*
+ * serialized XML data (server->client)
+ */
+type xmlDataOut = ^ array [] of MACH_MSG_TYPE_BYTE
+       ctype : xmlDataOut_t;
+
+/*
+ * additional types
+ */
+/*
+type task_move_send_t = MACH_MSG_TYPE_MOVE_SEND
+       ctype: mach_port_t;
+*/
+
+
+/*
+ * Connection management API's
+ */
+
+routine configopen     (       server          : mach_port_t;
+                               name            : xmlData;
+                        out    session         : mach_port_move_send_t;
+                        out    status          : int);
+
+routine configclose    (       server          : mach_port_t;
+                        out    status          : int);
+
+routine configlock     (       server          : mach_port_t;
+                        out    status          : int);
+
+routine configunlock   (       server          : mach_port_t;
+                        out    status          : int);
+
+       skip;   /* reserved for future use */
+       skip;   /* reserved for future use */
+       skip;   /* reserved for future use */
+       skip;   /* reserved for future use */
+
+/*
+ * Cache access API's
+ */
+
+routine configlist     (       server          : mach_port_t;
+                               xmlData         : xmlData;
+                               regexOptions    : int;
+                        out    list            : xmlDataOut, dealloc;
+                        out    status          : int);
+
+routine configadd      (       server          : mach_port_t;
+                               key             : xmlData;
+                               data            : xmlData;
+                        out    newInstance     : int;
+                        out    status          : int);
+
+routine configget      (       server          : mach_port_t;
+                               key             : xmlData;
+                        out    data            : xmlDataOut, dealloc;
+                        out    newInstance     : int;
+                        out    status          : int);
+
+routine configset      (       server          : mach_port_t;
+                               key             : xmlData;
+                               data            : xmlData;
+                               instance        : int;
+                        out    newInstance     : int;
+                        out    status          : int);
+
+routine configremove   (       server          : mach_port_t;
+                               key             : xmlData;
+                        out    status          : int);
+
+routine configtouch    (       server          : mach_port_t;
+                               key             : xmlData;
+                        out    status          : int);
+
+routine configadd_s    (       server          : mach_port_t;
+                               key             : xmlData;
+                               data            : xmlData;
+                        out    newInstance     : int;
+                        out    status          : int);
+
+       skip;   /* reserved for future use */
+       skip;   /* reserved for future use */
+       skip;   /* reserved for future use */
+
+/*
+ * Notification API's
+ */
+
+routine notifyadd      (       server          : mach_port_t;
+                               key             : xmlData;
+                               regexOptions    : int;
+                        out    status          : int);
+
+routine notifyremove   (       server          : mach_port_t;
+                               key             : xmlData;
+                               regexOptions    : int;
+                        out    status          : int);
+
+routine notifychanges  (       server          : mach_port_t;
+                        out    list            : xmlDataOut, dealloc;
+                        out    status          : int);
+
+routine notifyviaport  (       server          : mach_port_t;
+                               port            : mach_port_move_send_t;
+                               msgid           : mach_msg_id_t;
+                        out    status          : int);
+
+routine notifyviafd    (       server          : mach_port_t;
+                               path            : xmlData;
+                               identifier      : int;
+                        out    status          : int);
+
+routine notifyviasignal        (       server          : mach_port_t;
+                               task            : task_t /*task_move_send_t*/;
+                               sig             : int;
+                        out    status          : int);
+
+routine notifycancel   (       server          : mach_port_t;
+                        out    status          : int);
+
+       skip;   /* reserved for future use */
+       skip;   /* reserved for future use */
+       skip;   /* reserved for future use */
+       skip;   /* reserved for future use */
+
+/*
+ * Miscellaneous API's
+ */
+
+routine snapshot       (       server          : mach_port_t;
+                        out    status          : int);
diff --git a/SystemConfiguration.fproj/config_types.h b/SystemConfiguration.fproj/config_types.h
new file mode 100644 (file)
index 0000000..2351837
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#ifndef _CONFIG_TYPES_H
+#define _CONFIG_TYPES_H
+
+/*
+ * Keep IPC functions private to the framework
+ */
+#ifdef mig_external
+#undef mig_external
+#endif
+#define mig_external __private_extern__
+
+/*
+ * Mach server port name
+ */
+#define SCD_SERVER     "System Configuration Server"
+
+/*
+ * Input arguments: serialized key's, list delimiters, ...
+ *     (sent as out-of-line data in a message)
+ */
+typedef const char * xmlData_t;
+
+/* Output arguments: serialized data, lists, ...
+ *     (sent as out-of-line data in a message)
+ */
+typedef char * xmlDataOut_t;
+
+#endif /* !_CONFIG_TYPES_H */
diff --git a/SystemConfiguration.fproj/genSCPreferences.c b/SystemConfiguration.fproj/genSCPreferences.c
new file mode 100644 (file)
index 0000000..78bc8e4
--- /dev/null
@@ -0,0 +1,643 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+/*
+ * genSCPreferences.c
+ * - generates System Configuration header/cfile
+ * - invoke with "header" to generate the header
+ * - invoke with "cfile" to generate the cfile
+ */
+
+/*
+ * Modification History
+ * 3 Nov 2000  Dieter Siegmund (dieter@apple)
+ *             - created
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <mach/boolean.h>
+
+char copyright_string[] =
+"/*\n"
+" * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.\n"
+" *\n"
+" * @APPLE_LICENSE_HEADER_START@\n"
+" * \n"
+" * The contents of this file constitute Original Code as defined in and\n"
+" * are subject to the Apple Public Source License Version 1.1 (the\n"
+" * \"License\").  You may not use this file except in compliance with the\n"
+" * License.  Please obtain a copy of the License at\n"
+" * http://www.apple.com/publicsource and read it before using this file.\n"
+" * \n"
+" * This Original Code and all software distributed under the License are\n"
+" * distributed on an \"AS IS\" basis, WITHOUT WARRANTY OF ANY KIND, EITHER\n"
+" * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,\n"
+" * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,\n"
+" * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the\n"
+" * License for the specific language governing rights and limitations\n"
+" * under the License.\n"
+" * \n"
+" * @APPLE_LICENSE_HEADER_END@\n"
+" */\n";
+
+
+#define REGULAR                0
+#define COMMENT                1
+#define END            2
+
+#define STRING_MACRO_NAME      "STRING_DECL"
+
+#define KEY_PREFIX     "kSC"
+
+#define CACHE          "Cache"
+#define COMP           "Comp"
+#define PREF           "Pref"
+#define PROP           "Prop"
+#define PATH           "Path"
+#define NETENT         "EntNet"
+#define NETPROP                "PropNet"
+#define NETVAL         "ValNet"
+#define SETUPENT       "EntSetup"
+#define SETUPPROP      "PropSetup"
+#define SYSTEMENT      "EntSystem"
+#define SYSTEMPROP     "PropSystem"
+#define RESV           "Resv"
+#define USERSENT       "EntUsers"
+#define USERSPROP      "PropUsers"
+
+#define CFNUMBER       "CFNumber"
+#define CFSTRING       "CFString"
+#define CFNUMBER_BOOL  "CFNumber (0 or 1)"
+#define CFARRAY_CFSTRING "CFArray[CFString]"
+
+#define ACTIVE                 "Active"
+#define ADDRESSES              "Addresses"
+#define AIRPORT                        "AirPort"
+#define ALERT                  "Alert"
+#define ANYREGEX               "AnyRegex"
+#define AUTOMATIC              "Automatic"
+#define APPLETALK              "AppleTalk"
+#define AUTH                   "Auth"
+#define BINDINGMETHODS         "BindingMethods"
+#define BOOTP                  "BOOTP"
+#define BROADCAST              "Broadcast"
+#define BROADCASTADDRESSES     "BroadcastAddresses"
+#define BROADCASTSERVERTAG     "BroadcastServerTag"
+#define COMM                   "Comm"
+#define COMPONENTSEPARATOR     "ComponentSeparator"
+#define COMPUTERNAME           "ComputerName"
+#define CONFIGMETHOD           "ConfigMethod"
+#define CONSOLEUSER            "ConsoleUser"
+#define CURRENTSET             "CurrentSet"
+#define DEFAULTSERVERTAG       "DefaultServerTag"
+#define DEFAULTZONE            "DefaultZone"
+#define DESTADDRESSES          "DestAddresses"
+#define DHCP                   "DHCP"
+#define DHCPCLIENTID           "DHCPClientID"
+#define DEVICENAME             "DeviceName"
+#define DIALMODE               "DialMode"
+#define DNS                    "DNS"
+#define DOMAIN                         "Domain"
+#define DOMAINNAME             "DomainName"
+#define DOMAINSEPARATOR                "DomainSeparator"
+#define DUPLEX                 "Duplex"
+#define ENCODING               "Encoding"
+#define ENCRYPTION             "Encryption"
+#define ETHERNET               "Ethernet"
+#define EXCEPTIONSLIST         "ExceptionsList"
+#define FILE                   "File"
+#define FTPENABLE              "FTPEnable"
+#define FTPPASSIVE             "FTPPassive"
+#define FTPPORT                        "FTPPort"
+#define FTPPROXY               "FTPProxy"
+#define GID                    "GID"
+#define GLOBAL                 "Global"
+#define GOPHERENABLE           "GopherEnable"
+#define GOPHERPORT             "GopherPort"
+#define GOPHERPROXY            "GopherProxy"
+#define HARDWARE               "Hardware"
+#define HTTPENABLE             "HTTPEnable"
+#define HTTPPORT               "HTTPPort"
+#define HTTPPROXY              "HTTPProxy"
+#define INACTIVE               "Inactive"
+#define INCLUDEPRIVATENETS     "IncludePrivateNets"
+#define INFORM                 "INFORM"
+#define INTERFACE              "Interface"
+#define INTERFACES             "Interfaces"
+#define IPCP                   "IPCP"
+#define IPV4                   "IPv4"
+#define IPV6                   "IPv6"
+#define LASTUPDATED            "LastUpdated"
+#define LCP                    "LCP"
+#define LINK                   "Link"
+#define MACADDRESS             "MACAddress"
+#define MANUAL                 "Manual"
+#define MEDIA                  "Media"
+#define MODEM                  "Modem"
+#define NAME                   "Name"
+#define NETINFO                        "NetInfo"
+#define NETWORK                        "Network"
+#define NETWORKSERVICES                "NetworkServices"
+#define NETWORKID              "NetworkID"
+#define NIS                    "NIS"
+#define NODE                   "Node"
+#define NODEID                 "NodeID"
+#define PASSWORD               "Password"
+#define PLUGIN                 "Plugin"
+#define PORTNAME               "PortName"
+#define PPP                    "PPP"
+#define PPPOE                  "PPPoE"
+#define PPPSERIAL              "PPPSerial"
+#define PPPOVERRIDEPRIMARY     "PPPOverridePrimary"
+#define PREFS                  "Prefs"
+#define PRIMARYINTERFACE       "PrimaryInterface"
+#define PROTOCOL               "Protocol"
+#define PROXIES                        "Proxies"
+#define ROOTSEPARATOR          "RootSeparator"
+#define ROUTER                 "Router"
+#define RTSPENABLE             "RTSPEnable"
+#define RTSPPORT               "RTSPPort"
+#define RTSPPROXY              "RTSPProxy"
+#define SEARCHDOMAINS          "SearchDomains"
+#define SEEDNETWORKRANGE       "SeedNetworkRange"
+#define SEEDROUTER             "SeedRouter"
+#define SEEDZONES              "SeedZones"
+#define SERVICE                        "Service"
+#define SERVERADDRESSES                "ServerAddresses"
+#define SERVERTAGS             "ServerTags"
+#define SERVICEORDER           "ServiceOrder"
+#define SERVICEIDS             "ServiceIDs"
+#define SETS                   "Sets"
+#define SETUP                  "Setup"
+#define SPEED                  "Speed"
+#define STATE                  "State"
+#define SOCKSENABLE            "SOCKSEnable"
+#define SOCKSPORT              "SOCKSPort"
+#define SOCKSPROXY             "SOCKSProxy"
+#define SUBNETMASKS            "SubnetMasks"
+#define SUBTYPE                        "SubType"
+#define SYSTEM                 "System"
+#define TYPE                   "Type"
+#define UID                    "UID"
+#define USERS                  "Users"
+#define USERDEFINEDNAME                "UserDefinedName"
+#define VERBOSELOGGING         "VerboseLogging"
+
+struct {
+    int                                control;
+    unsigned char *            prefix;
+    unsigned char *            key;
+    unsigned char *            value;
+    unsigned char *            type;
+} names[] = {
+    { COMMENT, "/*\n * Reserved Keys\n */", NULL, NULL },
+    { REGULAR, RESV, LINK,     "__LINK__", CFSTRING },
+    { REGULAR, RESV, INACTIVE, "__INACTIVE__", NULL },
+    { COMMENT, "", NULL, NULL, NULL },
+
+    { COMMENT, "/*\n * Generic Keys\n */", NULL },
+    { REGULAR, PROP, MACADDRESS, NULL, CFSTRING },
+    { REGULAR, PROP, USERDEFINEDNAME, NULL, CFSTRING },
+    { COMMENT, "", NULL, NULL, NULL },
+
+    { COMMENT, "/*\n * Preference Keys\n */", NULL },
+    { REGULAR, PREF, CURRENTSET, NULL, NULL },
+    { REGULAR, PREF, HARDWARE, NULL, NULL },
+    { REGULAR, PREF, NETWORKSERVICES, NULL, NULL },
+    { REGULAR, PREF, SETS, NULL, NULL },
+    { REGULAR, PREF, SYSTEM, NULL, NULL },
+    { COMMENT, "", NULL, NULL, NULL },
+
+    { COMMENT, "/*\n * Component Keys\n */", NULL },
+    { REGULAR, COMP, NETWORK, NULL, NULL },
+    { REGULAR, COMP, SERVICE, NULL, NULL },
+    { REGULAR, COMP, GLOBAL, NULL, NULL },
+    { REGULAR, COMP, INTERFACE, NULL, NULL },
+    { REGULAR, COMP, SYSTEM, NULL, NULL },
+    { REGULAR, COMP, USERS, "users", NULL },   /* FIX ME! */
+    { COMMENT, "", NULL, NULL, NULL },
+
+    { COMMENT, "/*\n * Regex key which matches any component\n */", NULL },
+    { REGULAR, COMP, ANYREGEX, "[^/]+", NULL },
+    { COMMENT, "", NULL, NULL, NULL },
+
+    { COMMENT, "/*\n * Network Entity Keys\n */", NULL },
+    { REGULAR, NETENT, AIRPORT, NULL, NULL },
+    { REGULAR, NETENT, APPLETALK, NULL, NULL },
+    { REGULAR, NETENT, DNS, NULL, NULL },
+    { REGULAR, NETENT, ETHERNET, NULL, NULL },
+    { REGULAR, NETENT, INTERFACE, NULL, NULL },
+    { REGULAR, NETENT, IPV4, NULL, NULL },
+    { REGULAR, NETENT, IPV6, NULL, NULL },
+    { REGULAR, NETENT, LINK, NULL, NULL },
+    { REGULAR, NETENT, MODEM, NULL, NULL },
+    { REGULAR, NETENT, NETINFO, NULL, NULL },
+    { REGULAR, NETENT, NIS, NULL, NULL },
+    { REGULAR, NETENT, PPP, NULL, NULL },
+    { REGULAR, NETENT, PPPOE, NULL, NULL },
+    { REGULAR, NETENT, PROXIES, NULL, NULL },
+    { COMMENT, "", NULL, NULL, NULL },
+
+    { COMMENT, "/*\n * " NETWORK " Properties\n */", NULL },
+    { REGULAR, NETPROP, SERVICEORDER, NULL, CFARRAY_CFSTRING },
+    { REGULAR, NETPROP, PPPOVERRIDEPRIMARY, NULL, CFNUMBER_BOOL },
+    { COMMENT, "", NULL, NULL, NULL },
+
+    { COMMENT, "/*\n * " AIRPORT " (Hardware) Entity Keys\n */", NULL, NULL, NULL },
+    { REGULAR, NETPROP AIRPORT, "PowerEnabled", NULL, CFNUMBER_BOOL },
+    { REGULAR, NETPROP AIRPORT, AUTH PASSWORD, NULL, CFSTRING },
+    { REGULAR, NETPROP AIRPORT, AUTH PASSWORD ENCRYPTION, NULL, CFSTRING },
+    { REGULAR, NETPROP AIRPORT, "PreferredNetwork", NULL, CFSTRING },
+    { COMMENT, "", NULL, NULL, NULL },
+
+    { COMMENT, "/*\n * " APPLETALK " Entity Keys\n */", NULL, NULL, NULL },
+    { REGULAR, NETPROP APPLETALK, COMPUTERNAME, NULL, CFSTRING },
+    { REGULAR, NETPROP APPLETALK, COMPUTERNAME ENCODING, NULL, CFNUMBER },
+    { REGULAR, NETPROP APPLETALK, CONFIGMETHOD, NULL, CFSTRING },
+    { REGULAR, NETPROP APPLETALK, DEFAULTZONE, NULL, CFSTRING },
+    { REGULAR, NETPROP APPLETALK, NETWORKID, NULL, CFNUMBER },
+    { REGULAR, NETPROP APPLETALK, NODEID, NULL, CFNUMBER },
+    { REGULAR, NETPROP APPLETALK, SEEDNETWORKRANGE, NULL, CFARRAY_CFSTRING },
+    { REGULAR, NETPROP APPLETALK, SEEDZONES, NULL, CFARRAY_CFSTRING },
+    { COMMENT, "", NULL, NULL, NULL },
+    { COMMENT, "/* " KEY_PREFIX NETPROP APPLETALK CONFIGMETHOD " values */", NULL, NULL, NULL },
+    { REGULAR, NETVAL APPLETALK CONFIGMETHOD, NODE, NULL, NULL },
+    { REGULAR, NETVAL APPLETALK CONFIGMETHOD, ROUTER, NULL, NULL },
+    { REGULAR, NETVAL APPLETALK CONFIGMETHOD, SEEDROUTER, NULL, NULL },
+    { COMMENT, "", NULL, NULL, NULL },
+
+    { COMMENT, "/*\n * " DNS " Entity Keys\n */", NULL, NULL, NULL },
+    { REGULAR, NETPROP DNS, DOMAINNAME, NULL, CFSTRING },
+    { REGULAR, NETPROP DNS, SEARCHDOMAINS, NULL, CFARRAY_CFSTRING},
+    { REGULAR, NETPROP DNS, SERVERADDRESSES, NULL, CFARRAY_CFSTRING },
+    { COMMENT, "", NULL, NULL, NULL },
+
+    { COMMENT, "/*\n * " ETHERNET " (Hardware) Entity Keys\n */", NULL, NULL, NULL },
+    { COMMENT, "", NULL, NULL, NULL },
+
+    { COMMENT, "/*\n * " INTERFACE " Entity Keys\n */", NULL },
+    { REGULAR, NETPROP INTERFACE, DEVICENAME, NULL, CFSTRING },
+    { REGULAR, NETPROP INTERFACE, HARDWARE, NULL, CFSTRING },
+    { REGULAR, NETPROP INTERFACE, TYPE, NULL, CFSTRING },
+    { REGULAR, NETPROP INTERFACE, SUBTYPE, NULL, CFSTRING },
+    { COMMENT, "", NULL, NULL, NULL },
+    { COMMENT, "/* " KEY_PREFIX NETPROP INTERFACE TYPE " values */", NULL, NULL, NULL },
+    { REGULAR, NETVAL INTERFACE TYPE, ETHERNET, NULL, NULL },
+    { REGULAR, NETVAL INTERFACE TYPE, PPP, NULL, NULL },
+    { COMMENT, "", NULL, NULL, NULL },
+    { COMMENT, "/* " KEY_PREFIX NETPROP SERVICE SUBTYPE " values (for " PPP ") */", NULL, NULL, NULL },
+    { REGULAR, NETVAL INTERFACE SUBTYPE, PPPOE, NULL, NULL },
+    { REGULAR, NETVAL INTERFACE SUBTYPE, PPPSERIAL, NULL, NULL },
+    { COMMENT, "", NULL, NULL, NULL },
+
+    { COMMENT, "/*\n * " IPV4 " Entity Keys\n */", NULL, NULL, NULL },
+    { REGULAR, NETPROP IPV4, ADDRESSES, NULL, CFARRAY_CFSTRING },
+    { REGULAR, NETPROP IPV4, CONFIGMETHOD, NULL, CFSTRING },
+    { REGULAR, NETPROP IPV4, DHCPCLIENTID, NULL, CFSTRING },
+    { REGULAR, NETPROP IPV4, ROUTER, NULL, CFSTRING },
+    { REGULAR, NETPROP IPV4, SUBNETMASKS, NULL, CFARRAY_CFSTRING },
+    { REGULAR, NETPROP IPV4, DESTADDRESSES, NULL, CFARRAY_CFSTRING },
+    { REGULAR, NETPROP IPV4, BROADCASTADDRESSES, NULL, CFARRAY_CFSTRING },
+    { COMMENT, "", NULL, NULL, NULL },
+    { COMMENT, "/* " KEY_PREFIX NETPROP IPV4 CONFIGMETHOD " values */", NULL, NULL, NULL },
+    { REGULAR, NETVAL IPV4 CONFIGMETHOD, BOOTP, NULL, NULL },
+    { REGULAR, NETVAL IPV4 CONFIGMETHOD, DHCP, NULL, NULL },
+    { REGULAR, NETVAL IPV4 CONFIGMETHOD, INFORM, NULL, NULL },
+    { REGULAR, NETVAL IPV4 CONFIGMETHOD, MANUAL, NULL, NULL },
+    { REGULAR, NETVAL IPV4 CONFIGMETHOD, PPP, NULL, NULL },
+    { COMMENT, "", NULL, NULL, NULL },
+
+    { COMMENT, "/*\n * " IPV6 " Entity Keys\n */", NULL, NULL, NULL },
+    { REGULAR, NETPROP IPV6, ADDRESSES, NULL, CFARRAY_CFSTRING },
+    { REGULAR, NETPROP IPV6, CONFIGMETHOD, NULL, CFSTRING },
+    { COMMENT, "", NULL, NULL, NULL },
+
+    { COMMENT, "/*\n * " LINK " Entity Keys\n */", NULL, NULL, NULL },
+    { REGULAR, NETPROP LINK, ACTIVE, NULL, CFNUMBER_BOOL },
+    { COMMENT, "", NULL, NULL, NULL },
+
+    { COMMENT, "/*\n * " MODEM " (Hardware) Entity Keys\n */", NULL, NULL, NULL },
+    { REGULAR, NETPROP MODEM, "ConnectionScript", NULL, CFSTRING },
+    { REGULAR, NETPROP MODEM, DIALMODE, NULL, CFSTRING },
+    { REGULAR, NETPROP MODEM, "PulseDial", NULL, CFNUMBER_BOOL },
+    { REGULAR, NETPROP MODEM, "Speaker", NULL, CFNUMBER_BOOL },
+    { COMMENT, "", NULL, NULL, NULL },
+    { COMMENT, "/* " KEY_PREFIX NETPROP MODEM DIALMODE " values */", NULL, NULL, NULL },
+    { REGULAR, NETVAL MODEM DIALMODE, "IgnoreDialTone", NULL, NULL },
+    { REGULAR, NETVAL MODEM DIALMODE, MANUAL, NULL, NULL },
+    { REGULAR, NETVAL MODEM DIALMODE, "WaitForDialTone", NULL, NULL },
+    { COMMENT, "", NULL, NULL, NULL },
+
+    { COMMENT, "/*\n * " NETINFO " Entity Keys\n */", NULL, NULL, NULL },
+    { REGULAR, NETPROP NETINFO, BINDINGMETHODS, NULL, CFSTRING },
+    { REGULAR, NETPROP NETINFO, SERVERADDRESSES, NULL, CFARRAY_CFSTRING },
+    { REGULAR, NETPROP NETINFO, SERVERTAGS, NULL, CFARRAY_CFSTRING },
+    { REGULAR, NETPROP NETINFO, BROADCASTSERVERTAG, NULL, CFSTRING },
+    { COMMENT, "", NULL, NULL, NULL },
+    { COMMENT, "/* " KEY_PREFIX NETPROP NETINFO BINDINGMETHODS " values */", NULL, NULL, NULL },
+    { REGULAR, NETVAL NETINFO BINDINGMETHODS, BROADCAST, NULL, NULL },
+    { REGULAR, NETVAL NETINFO BINDINGMETHODS, DHCP, NULL, NULL },
+    { REGULAR, NETVAL NETINFO BINDINGMETHODS, MANUAL, NULL, NULL },
+    { COMMENT, "", NULL, NULL, NULL },
+    { COMMENT, "/* " KEY_PREFIX NETPROP NETINFO BROADCASTSERVERTAG " default value */", NULL, NULL, NULL },
+    { REGULAR, NETVAL NETINFO, DEFAULTSERVERTAG, "network", NULL },
+    { COMMENT, "", NULL, NULL, NULL },
+
+    { COMMENT, "/*\n * " NIS " Entity Keys\n */", NULL, NULL, NULL },
+    { REGULAR, NETPROP NIS, DOMAINNAME, NULL, CFSTRING },
+    { COMMENT, "", NULL, NULL, NULL },
+
+    { COMMENT, "/*\n * " PPP " Entity Keys\n */", NULL, NULL, NULL },
+    { REGULAR, NETPROP PPP, "DialOnDemand", NULL, CFNUMBER_BOOL },
+    { REGULAR, NETPROP PPP, "DisconnectOnIdle", NULL, CFNUMBER_BOOL },
+    { REGULAR, NETPROP PPP, "DisconnectOnIdleTimer", NULL, CFNUMBER },
+    { REGULAR, NETPROP PPP, "DisconnectOnLogout", NULL, CFNUMBER_BOOL },
+    { REGULAR, NETPROP PPP, "IdleReminderTimer", NULL, CFNUMBER },
+    { REGULAR, NETPROP PPP, "IdleReminder", NULL, CFNUMBER_BOOL },
+    { REGULAR, NETPROP PPP, "Logfile", NULL, CFSTRING },
+    { REGULAR, NETPROP PPP, VERBOSELOGGING, NULL, CFNUMBER_BOOL },
+    { COMMENT, "", NULL, NULL, NULL },
+
+    { COMMENT, "/* " AUTH ": */", NULL, NULL, NULL },
+    { REGULAR, NETPROP PPP, AUTH NAME, NULL, CFSTRING },
+    { REGULAR, NETPROP PPP, AUTH PASSWORD, NULL, CFSTRING },
+    { REGULAR, NETPROP PPP, AUTH PASSWORD ENCRYPTION, NULL, CFSTRING },
+    { REGULAR, NETPROP PPP, AUTH PROTOCOL, NULL, CFARRAY_CFSTRING },
+    { COMMENT, "", NULL, NULL, NULL },
+    { COMMENT, "/* " KEY_PREFIX NETPROP PPP AUTH PROTOCOL " values */", NULL, NULL, NULL },
+    { REGULAR, NETVAL PPP AUTH PROTOCOL, "CHAP", NULL, CFSTRING },
+    { REGULAR, NETVAL PPP AUTH PROTOCOL, "PAP", NULL, CFSTRING },
+
+    { COMMENT, "\n/* " COMM ": */", NULL, NULL, NULL },
+    { REGULAR, NETPROP PPP, COMM "AlternateRemoteAddress", NULL, CFSTRING },
+    { REGULAR, NETPROP PPP, COMM "ConnectDelay", NULL, CFNUMBER },
+    { REGULAR, NETPROP PPP, COMM "DisplayTerminalWindow", NULL, CFNUMBER_BOOL },
+    { REGULAR, NETPROP PPP, COMM "RedialCount", NULL, CFNUMBER },
+    { REGULAR, NETPROP PPP, COMM "RedialEnabled", NULL, CFNUMBER_BOOL },
+    { REGULAR, NETPROP PPP, COMM "RedialInterval", NULL, CFNUMBER },
+    { REGULAR, NETPROP PPP, COMM "RemoteAddress", NULL, CFSTRING },
+    { REGULAR, NETPROP PPP, COMM "TerminalScript", NULL, CFSTRING },
+
+    { COMMENT, "\n/* " IPCP ": */", NULL, NULL, NULL },
+    { REGULAR, NETPROP PPP, IPCP "CompressionVJ", NULL, CFNUMBER_BOOL },
+
+    { COMMENT, "\n/* " LCP ": */", NULL, NULL, NULL },
+    { REGULAR, NETPROP PPP, LCP "EchoEnabled", NULL, CFNUMBER_BOOL },
+    { REGULAR, NETPROP PPP, LCP "EchoFailure", NULL, CFNUMBER },
+    { REGULAR, NETPROP PPP, LCP "EchoInterval", NULL, CFNUMBER },
+    { REGULAR, NETPROP PPP, LCP "CompressionACField", NULL, CFNUMBER_BOOL },
+    { REGULAR, NETPROP PPP, LCP "CompressionPField", NULL, CFNUMBER_BOOL },
+    { REGULAR, NETPROP PPP, LCP "MRU", NULL, CFNUMBER },
+    { REGULAR, NETPROP PPP, LCP "MTU", NULL, CFNUMBER },
+    { REGULAR, NETPROP PPP, LCP "ReceiveACCM", NULL, CFNUMBER },
+    { REGULAR, NETPROP PPP, LCP "TransmitACCM", NULL, CFNUMBER },
+    { COMMENT, "", NULL, NULL, NULL },
+
+    { COMMENT, "/*\n * " PPPOE " Entity Keys\n */", NULL, NULL, NULL },
+    { COMMENT, "/* RESERVED FOR FUTURE USE */", NULL, NULL, NULL },
+    { COMMENT, "", NULL, NULL, NULL },
+
+    { COMMENT, "/*\n * " PPPSERIAL " Entity Keys\n */", NULL, NULL, NULL },
+    { COMMENT, "/* RESERVED FOR FUTURE USE */", NULL, NULL, NULL },
+    { COMMENT, "", NULL, NULL, NULL },
+
+    { COMMENT, "/*\n * " PROXIES " Entity Keys\n */", NULL, NULL, NULL },
+    { REGULAR, NETPROP PROXIES, EXCEPTIONSLIST, NULL, CFARRAY_CFSTRING },
+    { REGULAR, NETPROP PROXIES, FTPENABLE, NULL, CFNUMBER_BOOL },
+    { REGULAR, NETPROP PROXIES, FTPPASSIVE, NULL, CFNUMBER_BOOL },
+    { REGULAR, NETPROP PROXIES, FTPPORT, NULL, CFNUMBER },
+    { REGULAR, NETPROP PROXIES, FTPPROXY, NULL, CFSTRING },
+    { REGULAR, NETPROP PROXIES, GOPHERENABLE, NULL, CFNUMBER_BOOL },
+    { REGULAR, NETPROP PROXIES, GOPHERPORT, NULL, CFNUMBER },
+    { REGULAR, NETPROP PROXIES, GOPHERPROXY, NULL, CFSTRING },
+    { REGULAR, NETPROP PROXIES, HTTPENABLE, NULL, CFNUMBER_BOOL },
+    { REGULAR, NETPROP PROXIES, HTTPPORT, NULL, CFNUMBER },
+    { REGULAR, NETPROP PROXIES, HTTPPROXY, NULL, CFSTRING },
+    { REGULAR, NETPROP PROXIES, RTSPENABLE, NULL, CFNUMBER_BOOL },
+    { REGULAR, NETPROP PROXIES, RTSPPORT, NULL, CFNUMBER },
+    { REGULAR, NETPROP PROXIES, RTSPPROXY, NULL, CFSTRING },
+    { REGULAR, NETPROP PROXIES, SOCKSENABLE, NULL, CFNUMBER_BOOL },
+    { REGULAR, NETPROP PROXIES, SOCKSPORT, NULL, CFNUMBER },
+    { REGULAR, NETPROP PROXIES, SOCKSPROXY, NULL, CFSTRING },
+    { COMMENT, "", NULL, NULL, NULL },
+
+    { COMMENT, "/*\n * Users Entity Keys\n */", NULL, NULL, NULL },
+    { REGULAR, USERSENT, CONSOLEUSER, NULL, NULL },
+    { COMMENT, "", NULL, NULL, NULL },
+
+    { COMMENT, "/*\n " CONSOLEUSER " Entity Keys\n */", NULL, NULL, NULL },
+    { REGULAR, USERSPROP CONSOLEUSER, NAME, "username", CFSTRING },    /* FIX ME! */
+    { REGULAR, USERSPROP CONSOLEUSER, UID, "uid", CFSTRING },          /* FIX ME! */
+    { REGULAR, USERSPROP CONSOLEUSER, GID, "gid", CFSTRING },          /* FIX ME! */
+    { COMMENT, "", NULL, NULL, NULL },
+
+    { COMMENT, "/*\n * " SYSTEM " Entity Keys\n */", NULL, NULL, NULL },
+    { REGULAR, SYSTEMPROP, COMPUTERNAME, NULL, CFSTRING },
+    { REGULAR, SYSTEMPROP, COMPUTERNAME ENCODING, NULL, CFNUMBER },
+    { COMMENT, "", NULL, NULL, NULL },
+
+    { COMMENT, "/*\n * Configuration Cache Definitions\n */", NULL },
+    { COMMENT, "/* domain prefixes */", NULL },
+    { REGULAR, CACHE DOMAIN, FILE, "File:", NULL },
+    { REGULAR, CACHE DOMAIN, PLUGIN, "Plugin:", NULL },
+    { REGULAR, CACHE DOMAIN, SETUP, "Setup:", NULL },
+    { REGULAR, CACHE DOMAIN, STATE, "State:", NULL },
+    { REGULAR, CACHE DOMAIN, PREFS, "Prefs:", NULL },
+    { COMMENT, "", NULL, NULL, NULL },
+
+    { COMMENT, "/* Setup: properties */", NULL },
+    { REGULAR, CACHE SETUPPROP, CURRENTSET, NULL, NULL },
+    { REGULAR, CACHE SETUPPROP, LASTUPDATED, NULL, NULL },
+    { COMMENT, "", NULL, NULL, NULL },
+
+    { COMMENT, "/* properties */", NULL },
+    { REGULAR, CACHE NETPROP, INTERFACES, NULL, CFARRAY_CFSTRING },
+    { REGULAR, CACHE NETPROP, PRIMARYINTERFACE, NULL, CFSTRING },
+    { REGULAR, CACHE NETPROP, SERVICEIDS, NULL, CFARRAY_CFSTRING },
+    { COMMENT, "", NULL, NULL, NULL },
+
+// XXX OBSOLETE XXX
+    { COMMENT, "/* OBSOLETE " NETPROP AIRPORT ": */", NULL, NULL, NULL },
+    { REGULAR, NETPROP AIRPORT, INCLUDEPRIVATENETS, NULL, CFNUMBER_BOOL },
+    { REGULAR, NETPROP AIRPORT, "PreferredAirportNetwork", NULL, CFSTRING },
+
+    { COMMENT, "/* OBSOLETE " NETPROP ETHERNET ": */", NULL, NULL, NULL },
+    { REGULAR, NETPROP ETHERNET, SPEED, NULL, CFNUMBER },
+    { REGULAR, NETPROP ETHERNET, DUPLEX, NULL, CFSTRING },
+    { REGULAR, NETPROP ETHERNET, "WakeOnSignal", NULL, CFNUMBER_BOOL },
+    { REGULAR, NETPROP ETHERNET, "WakeOnTraffic", NULL, CFNUMBER_BOOL },
+    { COMMENT, "/* " KEY_PREFIX NETPROP ETHERNET DUPLEX " values */", NULL, NULL, NULL },
+    { REGULAR, NETVAL ETHERNET DUPLEX, AUTOMATIC, NULL, NULL },
+    { REGULAR, NETVAL ETHERNET DUPLEX, "FULL", NULL, NULL },
+    { REGULAR, NETVAL ETHERNET DUPLEX, "HALF", NULL, NULL },
+
+    { COMMENT, "/* OBSOLETE " NETPROP INTERFACE ": */", NULL, NULL, NULL },
+    { REGULAR, NETPROP INTERFACE, INTERFACE NAME, NULL, CFSTRING },
+    { REGULAR, NETPROP INTERFACE, MACADDRESS, NULL, CFSTRING },
+    { REGULAR, NETPROP INTERFACE, PORTNAME, NULL, CFSTRING },
+
+    { COMMENT, "/* OBSOLETE " NETPROP MODEM ": */", NULL, NULL, NULL },
+    { REGULAR, NETPROP MODEM, "IgnoreDialTone", NULL, CFNUMBER_BOOL },
+    { REGULAR, NETPROP MODEM, "InitString", NULL, CFNUMBER_BOOL },
+    { REGULAR, NETPROP MODEM, "Port", NULL, CFNUMBER_BOOL },
+    { REGULAR, NETPROP MODEM, PORTNAME, NULL, CFSTRING },
+    { REGULAR, NETPROP MODEM, "RedialCount", NULL, CFNUMBER_BOOL },
+    { REGULAR, NETPROP MODEM, "RedialEnabled", NULL, CFNUMBER_BOOL },
+    { REGULAR, NETPROP MODEM, "RedialTimeout", NULL, CFNUMBER_BOOL },
+    { REGULAR, NETPROP MODEM, "Script", NULL, CFSTRING },
+    { REGULAR, NETPROP MODEM, "SpeakerEnable", NULL, CFNUMBER },
+    { REGULAR, NETPROP MODEM, SPEED, NULL, CFNUMBER },
+    { REGULAR, NETPROP MODEM, "ToneDial", NULL, CFNUMBER },
+    { REGULAR, NETPROP MODEM, "WaitForTone", NULL, CFNUMBER },
+
+    { COMMENT, "/* OBSOLETE " NETPROP PPP ": */", NULL, NULL, NULL },
+    { REGULAR, NETPROP PPP, ALERT, NULL, CFARRAY_CFSTRING },
+    { REGULAR, NETVAL PPP ALERT, "Password", NULL, CFSTRING },
+    { REGULAR, NETVAL PPP ALERT, "Reminder", NULL, CFSTRING },
+    { REGULAR, NETVAL PPP ALERT, "Status", NULL, CFSTRING },
+    { REGULAR, NETPROP PPP, "CompressionEnable", NULL, CFNUMBER_BOOL },
+    { REGULAR, NETPROP PPP, "DeviceEntity", NULL, CFSTRING },
+    { REGULAR, NETPROP PPP, "HeaderCompression", NULL, CFNUMBER_BOOL },
+    { REGULAR, NETPROP PPP, "IdleDisconnect", NULL, CFNUMBER_BOOL },
+    { REGULAR, NETPROP PPP, "IdlePrompt", NULL, CFNUMBER_BOOL },
+    { REGULAR, NETPROP PPP, "IdleTimeout", NULL, CFSTRING },
+    { REGULAR, NETPROP PPP, "PromptTimeout", NULL, CFNUMBER },
+    { REGULAR, NETPROP PPP, "ReminderTimer", NULL, CFNUMBER },
+    { REGULAR, NETPROP PPP, "SessionTimer", NULL, CFNUMBER },
+    { REGULAR, NETPROP PPP, COMM "IdleTimer", NULL, CFNUMBER },
+    { REGULAR, NETPROP PPP, IPCP "LocalAddress", NULL, CFSTRING },
+    { REGULAR, NETPROP PPP, IPCP "RemoteAddress", NULL, CFSTRING },
+    { REGULAR, NETPROP PPP, IPCP "UseServerDNS", NULL, CFNUMBER_BOOL },
+
+    { COMMENT, "/* OBSOLETE " NETPROP PPPOE ": */", NULL, NULL, NULL },
+    { REGULAR, NETPROP PPPOE, PORTNAME, NULL, CFSTRING },
+
+    { COMMENT, "", NULL, NULL, NULL },
+// XXX OBSOLETE XXX
+
+    { END, NULL, NULL, NULL, NULL },
+};
+
+enum {
+    gen_extern_e,
+    gen_init_e,
+    gen_header_e,
+};
+
+void
+dump_names(int type)
+{
+    int i;
+
+    for (i = 0; TRUE; i++) {
+       switch (names[i].control) {
+           case END: {
+               goto done;
+               break;
+           }
+           case COMMENT: {
+               if (type != gen_extern_e && type != gen_init_e) {
+                   if (names[i].prefix)
+                       printf("%s\n", names[i].prefix);
+               }
+               break;
+           }
+           case REGULAR: {
+               char buf[256];
+
+               switch (type) {
+               case gen_header_e:
+                   snprintf(buf, sizeof(buf), KEY_PREFIX "%s%s;",
+                            names[i].prefix, names[i].key);
+
+                   if (names[i].type)
+                       printf(STRING_MACRO_NAME " %-40s /* %s */\n",
+                              buf, names[i].type);
+                   else
+                       printf(STRING_MACRO_NAME " %s\n", buf);
+                   break;
+               case gen_extern_e:
+                   snprintf(buf, sizeof(buf), KEY_PREFIX "%s%s",
+                            names[i].prefix, names[i].key);
+
+                   printf("volatile CFStringRef " KEY_PREFIX "%s%s = NULL;\n",
+                          names[i].prefix, names[i].key);
+                   break;
+               case gen_init_e:
+                   snprintf(buf, sizeof(buf), KEY_PREFIX "%s%s",
+                            names[i].prefix, names[i].key);
+                   if (names[i].value)
+                       printf("   *((void **)&%s) = (void *)CFSTR(\"%s\");\n",
+                              buf, names[i].value);
+                   else
+                       printf("   *((void **)&%s) = (void *)CFSTR(\"%s\");\n",
+                              buf, names[i].key);
+                   break;
+               default:
+                   break;
+               }
+           }
+           default: {
+               break;
+           }
+       }
+    }
+ done:
+    return;
+}
+
+int
+main(int argc, char * argv[])
+{
+    char * type = "";
+
+    if (argc >= 2)
+       type = argv[1];
+
+    if (strcmp(type, "header") == 0) {
+       printf("%s\n", copyright_string);
+       printf("/*\n * This file is automatically generated\n * DO NOT EDIT!\n */\n\n");
+       printf("#ifndef _SCPREFERENCES_H\n#define _SCPREFERENCES_H\n\n");
+       //printf("#ifndef " STRING_MACRO_NAME "\n");
+       printf("#ifndef __OBJC__\n");
+       printf("#define " STRING_MACRO_NAME "\t\textern const CFStringRef\n");
+       printf("#else\n");
+       printf("#define " STRING_MACRO_NAME "\t\textern NSString *\n");
+       printf("#endif\n");
+       //printf("#endif " STRING_MACRO_NAME "\n");
+       printf("\n");
+       dump_names(gen_header_e);
+       printf("#endif /* _SCPREFERENCES_H */\n");
+    }
+    else if (strcmp(type, "cfile") == 0) {
+       printf("/*\n * This file is automatically generated\n * DO NOT EDIT!\n */\n\n");
+       printf("\n#include <CoreFoundation/CFString.h>\n\n");
+       dump_names(gen_extern_e);
+       printf("\n\nvoid\n__private_extern__\n__Initialize(void)\n{\n");
+       dump_names(gen_init_e);
+       printf("}\n");
+    }
+    exit(0);
+    return (0);
+}
+
diff --git a/SystemConfiguration.fproj/h.template b/SystemConfiguration.fproj/h.template
new file mode 100644 (file)
index 0000000..f3c1b04
--- /dev/null
@@ -0,0 +1,11 @@
+$$
+/* $FILENAME$ created by $USERNAME$ on $DATE$ */
+
+#import <Foundation/Foundation.h>
+
+@interface $FILENAMESANSEXTENSION$ : NSObject
+{
+
+}
+
+@end
diff --git a/SystemConfiguration.fproj/m.template b/SystemConfiguration.fproj/m.template
new file mode 100644 (file)
index 0000000..1216fe5
--- /dev/null
@@ -0,0 +1,18 @@
+$$ Lines starting with $$ are not inserted into newly created files
+$$ The following substitutions are made:
+$$
+$$ $FILENAME$                e.g. foo.m
+$$ $FILENAMESANSEXTENSION$   e.g. foo
+$$ $DIRECTORY$               e.g. /tmp/MyNewApp
+$$ $PROJECTNAME$             e.g. MyNewApp
+$$ $SUBPROJECTNAME$          e.g. TheGoodPart.subproj
+$$ $USERNAME$                e.g. mwagner
+$$ $DATE$                    e.g. Jan-1-1994
+$$
+/* $FILENAME$ created by $USERNAME$ on $DATE$ */
+
+#import "$FILENAMESANSEXTENSION$.h"
+
+@implementation $FILENAMESANSEXTENSION$
+
+@end
diff --git a/SystemConfiguration.fproj/ppp.c b/SystemConfiguration.fproj/ppp.c
new file mode 100644 (file)
index 0000000..2af5760
--- /dev/null
@@ -0,0 +1,341 @@
+/*
+ * Copyright (c) 2001 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@
+ */
+
+#include <stdio.h>
+//#include <stdlib.h>
+//#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <SystemConfiguration/SystemConfiguration.h>
+
+
+#include "ppp_msg.h"
+#include "ppp.h"
+
+
+__private_extern__
+int
+PPPInit(int *ref)
+{
+       int                     sock;
+       int                     status;
+       struct sockaddr_un      sun;
+
+       sock = socket(AF_LOCAL, SOCK_STREAM, 0);
+
+       bzero(&sun, sizeof(sun));
+       sun.sun_family = AF_LOCAL;
+       strncpy(sun.sun_path, PPP_PATH, sizeof(sun.sun_path));
+
+       status = connect(sock,  (struct sockaddr *)&sun, sizeof(sun));
+       if (status < 0) {
+               return errno;
+       }
+
+       *ref = sock;
+       return 0;
+}
+
+
+__private_extern__
+int
+PPPDispose(int ref)
+{
+       if (close(ref) < 0) {
+               return errno;
+       }
+       return 0;
+}
+
+
+__private_extern__
+int
+PPPExec(int            ref,
+       u_long          link,
+       u_int32_t       cmd,
+       void            *request,
+       u_long          requestLen,
+       void            **reply,
+       u_long          *replyLen)
+{
+       struct ppp_msg_hdr      msg;
+       char                    *buf            = NULL;
+       ssize_t                 n;
+
+       bzero(&msg, sizeof(msg));
+       msg.m_type = cmd;
+       msg.m_link = link;
+       msg.m_len  = ((request != NULL) && (requestLen > 0)) ? requestLen : 0;
+
+       //  send the command
+       n = write(ref, &msg, sizeof(msg));
+       if (n == -1) {
+               SCDLog(LOG_ERR, CFSTR("PPPExec write() failed: %s"), strerror(errno));
+               return errno;
+       } else if (n != sizeof(msg)) {
+               SCDLog(LOG_ERR, CFSTR("PPPExec write() failed: wrote=%d"), n);
+               return -1;
+       }
+
+       if ((request != NULL) && (requestLen > 0)) {
+               n = write(ref, request, requestLen);
+               if (n == -1) {
+                       SCDLog(LOG_ERR, CFSTR("PPPExec write() failed: %s"), strerror(errno));
+                       return errno;
+               } else if (n != requestLen) {
+                       SCDLog(LOG_ERR, CFSTR("PPPExec write() failed: wrote=%d"), n);
+                       return -1;
+               }
+       }
+
+       // always expect a reply
+       n = read(ref, &msg, sizeof(msg));
+       if (n == -1) {
+               SCDLog(LOG_ERR, CFSTR("PPPExec read() failed: error=%s"), strerror(errno));
+               return errno;
+       } else if (n != sizeof(msg)) {
+               SCDLog(LOG_ERR, CFSTR("PPPExec read() failed: insufficent data, read=%d"), n);
+               return -1;
+       }
+
+       if (msg.m_len) {
+               buf = CFAllocatorAllocate(NULL, msg.m_len, 0);
+               if (buf) {
+                       // read reply
+                       n = read(ref, buf, msg.m_len);
+                       if (n == -1) {
+                               SCDLog(LOG_ERR, CFSTR("PPPExec read() failed: error=%s"), strerror(errno));
+                               CFAllocatorDeallocate(NULL, buf);
+                               return errno;
+                       } else if (n != msg.m_len) {
+                               SCDLog(LOG_ERR, CFSTR("PPPExec read() failed: insufficent data, read=%d"), n);
+                               CFAllocatorDeallocate(NULL, buf);
+                               return -1;
+                       }
+               }
+       }
+
+       if (reply && replyLen) {
+               *reply    = buf;
+               *replyLen = msg.m_len;
+       } else if (buf) {
+               // if additional returned data is unwanted
+               CFAllocatorDeallocate(NULL, buf);
+       }
+
+       return msg.m_result;
+}
+
+
+__private_extern__
+int
+PPPGetNumberOfLinks(int ref, u_long *nLinks)
+{
+       void    *replyBuf       = NULL;
+       u_long  replyBufLen     = 0;
+       int     status;
+
+       status = PPPExec(ref,
+                           -1,
+                           PPP_GETNBLINKS,
+                           NULL,
+                           0,
+                           &replyBuf,
+                           &replyBufLen);
+       if (status != 0) {
+               SCDLog(LOG_ERR, CFSTR("PPPExec() failed: status = %d"), status);
+               return status;
+       }
+
+       *nLinks = (replyBufLen == sizeof(u_long)) ? *(u_long *)replyBuf : 0;
+       if (replyBuf)   CFAllocatorDeallocate(NULL, replyBuf);
+
+       return status;
+}
+
+
+__private_extern__
+int
+PPPGetLinkByIndex(int ref, int index, u_int32_t *link)
+{
+       u_int32_t       i               = index;
+       void            *replyBuf       = NULL;
+       u_long          replyBufLen     = 0;
+       int             status;
+
+       status = PPPExec(ref,
+                           -1,
+                           PPP_GETLINKBYINDEX,
+                           (void *)&i,
+                           sizeof(i),
+                           &replyBuf,
+                           &replyBufLen);
+       if (status != 0) {
+               SCDLog(LOG_ERR, CFSTR("PPPExec() failed: status = %d"), status);
+               return status;
+       }
+
+       if (replyBuf && (replyBufLen == sizeof(u_int32_t))) {
+               *link = *(u_int32_t *)replyBuf;
+       } else {
+               status = -2;    /* if not found */
+       }
+       if (replyBuf)   CFAllocatorDeallocate(NULL, replyBuf);
+
+       return status;
+}
+
+
+__private_extern__
+int
+PPPGetLinkByServiceID(int ref, CFStringRef serviceID, u_int32_t *link)
+{
+       int             i;
+       u_long          nLinks;
+       int             status;
+       CFDataRef       sID;
+
+       sID = CFStringCreateExternalRepresentation(NULL,
+                                                  serviceID,
+                                                  kCFStringEncodingMacRoman,
+                                                  0);
+
+       status = PPPGetNumberOfLinks(ref, &nLinks);
+       if (status != 0) {
+               SCDLog(LOG_ERR, CFSTR("PPPGetNumberOfLinks() failed: %d"), status);
+               goto done;
+       }
+
+       status = -2;    /* assume no link */
+
+       for (i=0; i<nLinks; i++) {
+               u_int32_t       iLink;
+               void            *data   = NULL;
+               u_long          dataLen = 0;
+
+               status = PPPGetLinkByIndex(ref, i, &iLink);
+               if (status != 0) {
+                       SCDLog(LOG_ERR, CFSTR("PPPGetLinkByIndex() failed: %d"), status);
+                       goto done;
+               }
+
+               status = PPPGetOption(ref,
+                                     iLink,
+                                     PPP_OPT_SERVICEID,
+                                     &data,
+                                     &dataLen);
+               if (status != 0) {
+                       SCDLog(LOG_ERR, CFSTR("PPPGetOption() failed: %d"), status);
+                       goto done;
+               }
+
+               if ((dataLen != CFDataGetLength(sID)) ||
+                   (strncmp(data, CFDataGetBytePtr(sID), dataLen) != 0)) {
+                       /* if link not found */
+                       status = -2;
+               }
+
+               CFAllocatorDeallocate(NULL, data);
+               if (status == 0) {
+                       *link = iLink;
+                       goto done;
+               }
+       }
+
+    done :
+
+       CFRelease(sID);
+       return status;
+}
+
+
+__private_extern__
+int
+PPPGetOption(int ref, u_long link, u_long option, void **data, u_long *dataLen)
+{
+       struct ppp_opt_hdr      opt;
+       void                    *replyBuf       = NULL;
+       u_long                  replyBufLen     = 0;
+       int                     status;
+
+       bzero(&opt, sizeof(opt));
+       opt.o_type = option;
+
+       status = PPPExec(ref,
+                           link,
+                           PPP_GETOPTION,
+                           (void *)&opt,
+                           sizeof(opt),
+                           &replyBuf,
+                           &replyBufLen);
+       if (status != 0) {
+               SCDLog(LOG_ERR, CFSTR("PPPExec() failed: status = %d"), status);
+               *data = NULL;
+               *dataLen = 0;
+               return status;
+       }
+
+       if (replyBuf && (replyBufLen > sizeof(struct ppp_opt_hdr))) {
+               *dataLen = replyBufLen - sizeof(struct ppp_opt_hdr);
+               *data    = CFAllocatorAllocate(NULL, *dataLen, 0);
+               bcopy(((struct ppp_opt *)replyBuf)->o_data, *data, *dataLen);
+       }
+       if (replyBuf)   CFAllocatorDeallocate(NULL, replyBuf);
+
+       return status;
+}
+
+
+__private_extern__
+int
+PPPStatus(int ref, u_long link, struct ppp_status **stat)
+{
+       void    *replyBuf       = NULL;
+       u_long  replyBufLen     = 0;
+       int     status;
+
+       status = PPPExec(ref,
+                           link,
+                           PPP_STATUS,
+                           NULL,
+                           0,
+                           &replyBuf,
+                           &replyBufLen);
+       if (status != 0) {
+               SCDLog(LOG_ERR, CFSTR("PPPExec() failed: status = %d"), status);
+               return status;
+       }
+
+       if (replyBuf && (replyBufLen == sizeof(struct ppp_status))) {
+               *stat = (struct ppp_status *)replyBuf;
+       } else {
+               if (replyBuf)   CFAllocatorDeallocate(NULL, replyBuf);
+               *stat = NULL;
+               status = -1;
+       }
+
+       return status;
+}
diff --git a/SystemConfiguration.fproj/ppp.h b/SystemConfiguration.fproj/ppp.h
new file mode 100644 (file)
index 0000000..8375d1b
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2001 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@
+ */
+
+#ifndef _PPP_H
+#define _PPP_H
+
+
+#include <sys/cdefs.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include "ppp_msg.h"
+
+
+__BEGIN_DECLS
+
+int            PPPInit                 (int                    *ref);
+
+int            PPPDispose              (int                    ref);
+
+int            PPPExec                 (int                    ref,
+                                        u_long                 link,
+                                        u_int32_t              cmd,
+                                        void                   *request,
+                                        u_long                 requestLen,
+                                        void                   **reply,
+                                        u_long                 *replyLen);
+
+int            PPPGetNumberOfLinks     (int                    ref,
+                                        u_long                 *nLinks);
+
+int            PPPGetLinkByIndex       (int                    ref,
+                                        int                    index,
+                                        u_int32_t              *link);
+
+int            PPPGetLinkByServiceID   (int                    ref,
+                                        CFStringRef            serviceID,
+                                        u_int32_t              *link);
+
+int            PPPGetOption            (int                    ref,
+                                        u_long                 link,
+                                        u_long                 option,
+                                        void                   **data,
+                                        u_long                 *dataLen);
+
+int            PPPStatus               (int                    ref,
+                                        u_long                 link,
+                                        struct ppp_status      **stat);
+
+__END_DECLS
+
+#endif /* _PPP_H */
diff --git a/SystemConfiguration.fproj/ppp_msg.h b/SystemConfiguration.fproj/ppp_msg.h
new file mode 100644 (file)
index 0000000..4a3c6da
--- /dev/null
@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+
+#ifndef        _PPP_MSG_H
+#define        _PPP_MSG_H
+
+#include <sys/types.h>
+
+
+/* local socket path */
+#define PPP_PATH       "/var/run/pppconfd\0"
+
+
+/* PPP message paquets */
+struct ppp_msg_hdr {
+    u_int32_t          m_type;         // type of the message
+    u_int32_t          m_result;       // error code of notification message
+    u_int32_t          m_cookie;       // user param
+    u_int32_t          m_link;         // link for this message
+    u_int32_t          m_len;          // len of the following data
+};
+
+struct ppp_msg {
+    u_int32_t          m_type;         // type of the message
+    u_int32_t          m_result;       // error code of notification message
+    u_int32_t          m_cookie;       // user param, or error num for event
+    u_int32_t          m_link;         // link for this message
+    u_int32_t          m_len;          // len of the following data
+    u_char             m_data[1];      // msg data sent or received
+};
+
+
+
+/* codes for ppp messages */
+enum {
+    /* API client commands */
+    PPP_VERSION = 1,
+    PPP_STATUS,
+    PPP_CONNECT,
+    PPP_DISCONNECT = 5,
+    PPP_GETOPTION,
+    PPP_SETOPTION,
+    PPP_ENABLE_EVENT,
+    PPP_DISABLE_EVENT,
+    PPP_EVENT,
+    PPP_GETNBLINKS,
+    PPP_GETLINKBYINDEX
+
+};
+
+// struct for an option
+struct ppp_opt_hdr {
+    u_int32_t          o_type;
+};
+
+struct ppp_opt {
+    u_int32_t          o_type;
+    u_char             o_data[1];
+};
+
+
+/* codes for options management */
+enum {
+
+    PPP_OPT_DEV_NAME = 1,              // string
+    PPP_OPT_DEV_SPEED,                 // 4 bytes
+    PPP_OPT_DEV_CONNECTSCRIPT,         // string
+
+    PPP_OPT_COMM_IDLETIMER,            // 4 bytes
+    PPP_OPT_COMM_REMOTEADDR,           // string
+
+    PPP_OPT_AUTH_PROTO,                        // 4 bytes
+    PPP_OPT_AUTH_NAME,                 // string
+    PPP_OPT_AUTH_PASSWD,               // string
+
+    PPP_OPT_LCP_HDRCOMP,               // 4 bytes
+    PPP_OPT_LCP_MRU,                   // 4 bytes
+    PPP_OPT_LCP_MTU,                   // 4 bytes
+    PPP_OPT_LCP_RCACCM,                        // 4 bytes
+    PPP_OPT_LCP_TXACCM,                        // 4 bytes
+
+    PPP_OPT_IPCP_HDRCOMP,              // 4 bytes
+    PPP_OPT_IPCP_LOCALADDR,            // 4 bytes
+    PPP_OPT_IPCP_REMOTEADDR,           // 4 bytes
+
+    PPP_OPT_LOGFILE,                   // string
+    PPP_OPT_RESERVED,                  // 4 bytes
+    PPP_OPT_REMINDERTIMER,             // 4 bytes (not implemented)
+    PPP_OPT_ALERTENABLE,               // 4 bytes (not implemented)
+
+    PPP_OPT_LCP_ECHO,                  // struct ppp_opt_echo
+
+    PPP_OPT_COMM_CONNECTDELAY,         // 4 bytes
+    PPP_OPT_COMM_SESSIONTIMER,         // 4 bytes
+    PPP_OPT_COMM_TERMINALMODE,         // 4 bytes
+    PPP_OPT_COMM_TERMINALSCRIPT,       // string. Additionnal connection script, once modem is connected
+    PPP_OPT_DEV_CAPS,                  // struct ppp_caps...
+
+    PPP_OPT_IPCP_USESERVERDNS,         // 4 bytes
+    PPP_OPT_COMM_CONNECTSPEED,         // 4 bytes, actual connection speed
+    PPP_OPT_SERVICEID                  // string, name of the associated service in the cache
+
+};
+
+// options values
+
+// PPP_LCP_OPT_HDRCOMP -- option ppp addr/ctrl compression
+enum {
+    PPP_LCP_HDRCOMP_NONE = 0,
+    PPP_LCP_HDRCOMP_ADDR = 1,
+    PPP_LCP_HDRCOMP_PROTO = 2
+};
+
+enum {
+    PPP_COMM_TERM_NONE = 0,
+    PPP_COMM_TERM_SCRIPT,
+    PPP_COMM_TERM_WINDOW
+};
+
+enum {
+    PPP_IPCP_HDRCOMP_NONE = 0,
+    PPP_IPCP_HDRCOMP_VJ
+};
+
+// PPP_LCP_OPT_RCACCM -- option receive control asynchronous character map
+enum {
+    PPP_LCP_ACCM_NONE = 0,
+    PPP_LCP_ACCM_XONXOFF = 0x000A0000,
+    PPP_LCP_ACCM_ALL = 0xFFFFFFFF
+};
+
+// PPP_OPT_AUTH
+enum {
+    PPP_AUTH_NONE = 0,
+    PPP_AUTH_PAPCHAP,
+    PPP_AUTH_PAP,
+    PPP_AUTH_CHAP
+};
+
+// state machine
+enum {
+    PPP_IDLE = 0,
+    PPP_INITIALIZE,
+    PPP_CONNECTLINK,
+    PPP_STATERESERVED,
+    PPP_ESTABLISH,
+    PPP_AUTHENTICATE,
+    PPP_CALLBACK,
+    PPP_NETWORK,
+    PPP_RUNNING,
+    PPP_TERMINATE,
+    PPP_DISCONNECTLINK
+};
+
+// events
+enum {
+    PPP_EVT_DISCONNECTED = 1,
+    PPP_EVT_CONNSCRIPT_STARTED,
+    PPP_EVT_CONNSCRIPT_FINISHED,
+    PPP_EVT_TERMSCRIPT_STARTED,
+    PPP_EVT_TERMSCRIPT_FINISHED,
+    PPP_EVT_LOWERLAYER_UP,
+    PPP_EVT_LOWERLAYER_DOWN,
+    PPP_EVT_LCP_UP,
+    PPP_EVT_LCP_DOWN,
+    PPP_EVT_IPCP_UP,
+    PPP_EVT_IPCP_DOWN,
+    PPP_EVT_AUTH_STARTED,
+    PPP_EVT_AUTH_FAILED,
+    PPP_EVT_AUTH_SUCCEDED
+};
+
+struct ppp_opt_echo {          // 0 for the following value will cancel echo option
+    u_int16_t  interval;       // delay in seconds between echo requests
+    u_int16_t  failure;        // # of failure before declaring the link down
+};
+
+struct ppp_status {
+    // connection stats
+    u_int32_t          status;
+    union {
+       struct connected {
+           u_int32_t           timeElapsed;
+           u_int32_t           timeRemaining;
+           // bytes stats
+           u_int32_t           inBytes;
+           u_int32_t           inPackets;
+           u_int32_t           inErrors;
+           u_int32_t           outBytes;
+           u_int32_t           outPackets;
+           u_int32_t           outErrors;
+       } run;
+       struct disconnected {
+           u_int32_t           lastDiscCause;
+       } disc;
+    } s;
+};
+
+enum {
+    // from 0 to 255, we use bsd error codes from errno.h
+
+    // ppp speficic error codes
+    PPP_ERR_GEN_ERROR  = 256,
+    PPP_ERR_CONNSCRIPTFAILED,
+    PPP_ERR_TERMSCRIPTFAILED,
+    PPP_ERR_LCPFAILED,
+    PPP_ERR_AUTHFAILED,
+    PPP_ERR_IDLETIMEOUT,
+    PPP_ERR_SESSIONTIMEOUT,
+    PPP_ERR_LOOPBACK,
+    PPP_ERR_PEERDEAD,
+    PPP_ERR_DISCSCRIPTFAILED,
+
+    // modem specific error codes
+    PPP_ERR_MOD_NOCARRIER      = 512,
+    PPP_ERR_MOD_BUSY,
+    PPP_ERR_MOD_NODIALTONE,
+    PPP_ERR_MOD_ERROR
+};
+
+#endif /* _PPP_MSG_H */
+
diff --git a/configd.tproj/Makefile b/configd.tproj/Makefile
new file mode 100644 (file)
index 0000000..d9c7c6f
--- /dev/null
@@ -0,0 +1,60 @@
+#
+# Generated by the Apple Project Builder.
+#
+# NOTE: Do NOT change this file -- Project Builder maintains it.
+#
+# Put all of your customizations in files called Makefile.preamble
+# and Makefile.postamble (both optional), and Makefile will include them.
+#
+
+NAME = configd
+
+PROJECTVERSION = 2.8
+PROJECT_TYPE = Tool
+
+HFILES = configd.h _SCD.h configd_server.h notify_server.h\
+        plugin_support.h session.h notify.h
+
+MFILES = configd.m
+
+CFILES = _SCD.c configd_server.c notify_server.c plugin_support.c\
+        session.c notify.c _configopen.c _configclose.c _configlock.c\
+        _configunlock.c _configlist.c _configadd.c _configadd_s.c\
+        _configget.c _configset.c _configremove.c _configtouch.c\
+        _notifyadd.c _notifyremove.c _notifychanges.c _notifyviaport.c\
+        _notifyviafd.c _notifyviasignal.c _notifycancel.c _snapshot.c
+
+OTHERSRCS = Makefile.preamble Makefile Makefile.postamble m.template\
+           h.template config.defs
+
+
+MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles
+CODE_GEN_STYLE = DYNAMIC
+MAKEFILE = tool.make
+NEXTSTEP_INSTALLDIR = /usr/sbin
+WINDOWS_INSTALLDIR = /Library/Executables
+PDO_UNIX_INSTALLDIR = /bin
+LIBS = -lobjc
+DEBUG_LIBS = $(LIBS)
+PROF_LIBS = $(LIBS)
+
+
+FRAMEWORKS = -framework SystemConfiguration
+
+
+NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc
+WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc
+PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc
+NEXTSTEP_JAVA_COMPILER = /usr/bin/javac
+WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe
+PDO_UNIX_JAVA_COMPILER = $(JDKBINDIR)/javac
+
+include $(MAKEFILEDIR)/platform.make
+
+-include Makefile.preamble
+
+include $(MAKEFILEDIR)/$(MAKEFILE)
+
+-include Makefile.postamble
+
+-include Makefile.dependencies
diff --git a/configd.tproj/Makefile.postamble b/configd.tproj/Makefile.postamble
new file mode 100644 (file)
index 0000000..ce9e54f
--- /dev/null
@@ -0,0 +1,101 @@
+###############################################################################
+#  Makefile.postamble
+#  Copyright 1997, Apple Computer, Inc.
+#
+#  Use this makefile, which is imported after all other makefiles, to
+#  override attributes for a project's Makefile environment. This allows you
+#  to take advantage of the environment set up by the other Makefiles.
+#  You can also define custom rules at the end of this file.
+#
+###############################################################################
+#
+# These variables are exported by the standard makefiles and can be
+# used in any customizations you make.  They are *outputs* of
+# the Makefiles and should be used, not set.
+#
+#  PRODUCTS: products to install.  All of these products will be placed in
+#       the directory $(DSTROOT)$(INSTALLDIR)
+#  GLOBAL_RESOURCE_DIR: The directory to which resources are copied.
+#  LOCAL_RESOURCE_DIR: The directory to which localized resources are copied.
+#  OFILE_DIR: Directory into which .o object files are generated.
+#  DERIVED_SRC_DIR: Directory used for all other derived files
+#
+#  ALL_CFLAGS:  flags to pass when compiling .c files
+#  ALL_MFLAGS:  flags to pass when compiling .m files
+#  ALL_CCFLAGS:  flags to pass when compiling .cc, .cxx, and .C files
+#  ALL_MMFLAGS:  flags to pass when compiling .mm, .mxx, and .M files
+#  ALL_PRECOMPFLAGS:  flags to pass when precompiling .h files
+#  ALL_LDFLAGS:  flags to pass when linking object files
+#  ALL_LIBTOOL_FLAGS:  flags to pass when libtooling object files
+#  ALL_PSWFLAGS:  flags to pass when processing .psw and .pswm (pswrap) files
+#  ALL_RPCFLAGS:  flags to pass when processing .rpc (rpcgen) files
+#  ALL_YFLAGS:  flags to pass when processing .y (yacc) files
+#  ALL_LFLAGS:  flags to pass when processing .l (lex) files
+#
+#  NAME: name of application, bundle, subproject, palette, etc.
+#  LANGUAGES: langages in which the project is written (default "English")
+#  English_RESOURCES: localized resources (e.g. nib's, images) of project
+#  GLOBAL_RESOURCES: non-localized resources of project
+#
+#  SRCROOT:  base directory in which to place the new source files
+#  SRCPATH:  relative path from SRCROOT to present subdirectory
+#
+#  INSTALLDIR: Directory the product will be installed into by 'install' target
+#  PUBLIC_HDR_INSTALLDIR: where to install public headers.  Don't forget
+#        to prefix this with DSTROOT when you use it.
+#  PRIVATE_HDR_INSTALLDIR: where to install private headers.  Don't forget
+#       to prefix this with DSTROOT when you use it.
+#
+#  EXECUTABLE_EXT: Executable extension for the platform (i.e. .exe on Windows)
+#
+###############################################################################
+
+# Some compiler flags can be overridden here for certain build situations.
+#
+#    WARNING_CFLAGS:  flag used to set warning level (defaults to -Wmost)
+#    DEBUG_SYMBOLS_CFLAGS:  debug-symbol flag passed to all builds (defaults
+#      to -g)
+#    DEBUG_BUILD_CFLAGS:  flags passed during debug builds (defaults to -DDEBUG)
+#    OPTIMIZE_BUILD_CFLAGS:  flags passed during optimized builds (defaults
+#      to -O)
+#    PROFILE_BUILD_CFLAGS:  flags passed during profile builds (defaults
+#      to -pg -DPROFILE)
+#    LOCAL_DIR_INCLUDE_DIRECTIVE:  flag used to add current directory to
+#      the include path (defaults to -I.)
+#    DEBUG_BUILD_LDFLAGS, OPTIMIZE_BUILD_LDFLAGS, PROFILE_BUILD_LDFLAGS: flags
+#      passed to ld/libtool (defaults to nothing)
+
+
+# Library and Framework projects only:
+#    INSTALL_NAME_DIRECTIVE:  This directive ensures that executables linked
+#      against the framework will run against the correct version even if
+#      the current version of the framework changes.  You may override this
+#      to "" as an alternative to using the DYLD_LIBRARY_PATH during your
+#      development cycle, but be sure to restore it before installing.
+
+
+# Ownership and permissions of files installed by 'install' target
+
+#INSTALL_AS_USER = root
+       # User/group ownership
+#INSTALL_AS_GROUP = wheel
+       # (probably want to set both of these)
+#INSTALL_PERMISSIONS =
+       # If set, 'install' chmod's executable to this
+
+
+# Options to strip.  Note: -S strips debugging symbols (executables can be stripped
+# down further with -x or, if they load no bundles, with no options at all).
+
+#STRIPFLAGS = -S
+
+
+#########################################################################
+# Put rules to extend the behavior of the standard Makefiles here.  Include them in
+# the dependency tree via cvariables like AFTER_INSTALL in the Makefile.preamble.
+#
+# You should avoid redefining things like "install" or "app", as they are
+# owned by the top-level Makefile API and no context has been set up for where
+# derived files should go.
+#
+
diff --git a/configd.tproj/Makefile.preamble b/configd.tproj/Makefile.preamble
new file mode 100644 (file)
index 0000000..b5e7557
--- /dev/null
@@ -0,0 +1,141 @@
+###############################################################################
+#  Makefile.preamble
+#  Copyright 1997, Apple Computer, Inc.
+#
+#  Use this makefile for configuring the standard application makefiles
+#  associated with ProjectBuilder. It is included before the main makefile.
+#  In Makefile.preamble you set attributes for a project, so they are available
+#  to the project's makefiles.  In contrast, you typically write additional rules or
+#  override built-in behavior in the Makefile.postamble.
+#
+#  Each directory in a project tree (main project plus subprojects) should
+#  have its own Makefile.preamble and Makefile.postamble.
+###############################################################################
+#
+# Before the main makefile is included for this project, you may set:
+#
+#    MAKEFILEDIR: Directory in which to find $(MAKEFILE)
+#    MAKEFILE: Top level mechanism Makefile (e.g., app.make, bundle.make)
+
+# Compiler/linker flags added to the defaults:  The OTHER_* variables will be
+# inherited by all nested sub-projects, but the LOCAL_ versions of the same
+# variables will not.  Put your -I, -D, -U, and -L flags in ProjectBuilder's
+# Build Attributes inspector if at all possible.  To override the default flags
+# that get passed to ${CC} (e.g. change -O to -O2), see Makefile.postamble.  The
+# variables below are *inputs* to the build process and distinct from the override
+# settings done (less often) in the Makefile.postamble.
+#
+#    OTHER_CFLAGS, LOCAL_CFLAGS:  additional flags to pass to the compiler
+#      Note that $(OTHER_CFLAGS) and $(LOCAL_CFLAGS) are used for .h, ...c, .m,
+#      .cc, .cxx, .C, and .M files.  There is no need to respecify the
+#      flags in OTHER_MFLAGS, etc.
+#    OTHER_MFLAGS, LOCAL_MFLAGS:  additional flags for .m files
+#    OTHER_CCFLAGS, LOCAL_CCFLAGS:  additional flags for .cc, .cxx, and ...C files
+#    OTHER_MMFLAGS, LOCAL_MMFLAGS:  additional flags for .mm and .M files
+#    OTHER_PRECOMPFLAGS, LOCAL_PRECOMPFLAGS:  additional flags used when
+#      precompiling header files
+#    OTHER_LDFLAGS, LOCAL_LDFLAGS:  additional flags passed to ld and libtool
+#    OTHER_PSWFLAGS, LOCAL_PSWFLAGS:  additional flags passed to pswrap
+#    OTHER_RPCFLAGS, LOCAL_RPCFLAGS:  additional flags passed to rpcgen
+#    OTHER_YFLAGS, LOCAL_YFLAGS:  additional flags passed to yacc
+#    OTHER_LFLAGS, LOCAL_LFLAGS:  additional flags passed to lex
+
+# These variables provide hooks enabling you to add behavior at almost every
+# stage of the make:
+#
+#    BEFORE_PREBUILD: targets to build before installing headers for a subproject
+#    AFTER_PREBUILD: targets to build after installing headers for a subproject
+#    BEFORE_BUILD_RECURSION: targets to make before building subprojects
+#    BEFORE_BUILD: targets to make before a build, but after subprojects
+#    AFTER_BUILD: targets to make after a build
+#
+#    BEFORE_INSTALL: targets to build before installing the product
+#    AFTER_INSTALL: targets to build after installing the product
+#    BEFORE_POSTINSTALL: targets to build before postinstalling every subproject
+#    AFTER_POSTINSTALL: targts to build after postinstalling every subproject
+#
+#    BEFORE_INSTALLHDRS: targets to build before installing headers for a
+#         subproject
+#    AFTER_INSTALLHDRS: targets to build after installing headers for a subproject
+#    BEFORE_INSTALLSRC: targets to build before installing source for a subproject
+#    AFTER_INSTALLSRC: targets to build after installing source for a subproject
+#
+#    BEFORE_DEPEND: targets to build before building dependencies for a
+#        subproject
+#    AFTER_DEPEND: targets to build after building dependencies for a
+#        subproject
+#
+#    AUTOMATIC_DEPENDENCY_INFO: if YES, then the dependency file is
+#        updated every time the project is built.  If NO, the dependency
+#        file is only built when the depend target is invoked.
+
+# Framework-related variables:
+#    FRAMEWORK_DLL_INSTALLDIR:  On Windows platforms, this variable indicates
+#      where to put the framework's DLL.  This variable defaults to
+#      $(INSTALLDIR)/../Executables
+
+# Library-related variables:
+#    PUBLIC_HEADER_DIR:  Determines where public exported header files
+#      should be installed.  Do not include $(DSTROOT) in this value --
+#      it is prefixed automatically.  For library projects you should
+#       set this to something like /Developer/Headers/$(NAME).  Do not set
+#       this variable for framework projects unless you do not want the
+#       header files included in the framework.
+#    PRIVATE_HEADER_DIR:  Determines where private exported header files
+#      should be installed.  Do not include $(DSTROOT) in this value --
+#      it is prefixed automatically.
+#    LIBRARY_STYLE:  This may be either STATIC or DYNAMIC, and determines
+#      whether the libraries produced are statically linked when they
+#      are used or if they are dynamically loadable. This defaults to
+#       DYNAMIC.
+#    LIBRARY_DLL_INSTALLDIR:  On Windows platforms, this variable indicates
+#      where to put the library's DLL.  This variable defaults to
+#      $(INSTALLDIR)/../Executables
+#
+#    INSTALL_AS_USER: owner of the intalled products (default root)
+#    INSTALL_AS_GROUP: group of the installed products (default wheel)
+#    INSTALL_PERMISSIONS: permissions of the installed product (default o+rX)
+#
+#    OTHER_RECURSIVE_VARIABLES: The names of variables which you want to be
+#      passed on the command line to recursive invocations of make.  Note that
+#      the values in OTHER_*FLAGS are inherited by subprojects automatically --
+#      you do not have to (and shouldn't) add OTHER_*FLAGS to
+#      OTHER_RECURSIVE_VARIABLES.
+
+# Additional headers to export beyond those in the PB.project:
+#    OTHER_PUBLIC_HEADERS
+#    OTHER_PROJECT_HEADERS
+#    OTHER_PRIVATE_HEADERS
+
+# Additional files for the project's product: <<path relative to proj?>>
+#    OTHER_RESOURCES: (non-localized) resources for this project
+#    OTHER_OFILES: relocatables to be linked into this project
+#    OTHER_LIBS: more libraries to link against
+#    OTHER_PRODUCT_DEPENDS: other dependencies of this project
+#    OTHER_SOURCEFILES: other source files maintained by .pre/postamble
+#    OTHER_GARBAGE: additional files to be removed by `make clean'
+
+# Set this to YES if you don't want a final libtool call for a library/framework.
+#    BUILD_OFILES_LIST_ONLY
+
+# To include a version string, project source must exist in a directory named
+# $(NAME).%d[.%d][.%d] and the following line must be uncommented.
+OTHER_GENERATED_OFILES = $(VERS_OFILE)
+
+# This definition will suppress stripping of debug symbols when an executable
+# is installed.  By default it is YES.
+# STRIP_ON_INSTALL = NO
+
+# Uncomment to suppress generation of a KeyValueCoding index when installing
+# frameworks (This index is used by WOB and IB to determine keys available
+# for an object).  Set to YES by default.
+# PREINDEX_FRAMEWORK = NO
+
+# Change this definition to install projects somewhere other than the
+# standard locations.  NEXT_ROOT defaults to "C:/Apple" on Windows systems
+# and "" on other systems.
+# DSTROOT = $(HOME)
+
+# Additional flags (MiG generated files)
+OTHER_OFILES = configServer.o
+
diff --git a/configd.tproj/PB.project b/configd.tproj/PB.project
new file mode 100644 (file)
index 0000000..9174d1f
--- /dev/null
@@ -0,0 +1,75 @@
+{
+    DYNAMIC_CODE_GEN = YES;
+    FILESTABLE = {
+       FRAMEWORKS = (SystemConfiguration.framework);
+       FRAMEWORKSEARCH = ();
+       HEADERSEARCH = ();
+       H_FILES = (
+           configd.h,
+           _SCD.h,
+           configd_server.h,
+           notify_server.h,
+           plugin_support.h,
+           session.h,
+           notify.h
+       );
+       OTHER_LIBS = (objc);
+       OTHER_LINKED = (
+           configd.m,
+           _SCD.c,
+           configd_server.c,
+           notify_server.c,
+           plugin_support.c,
+           session.c,
+           notify.c,
+           _configopen.c,
+           _configclose.c,
+           _configlock.c,
+           _configunlock.c,
+           _configlist.c,
+           _configadd.c,
+           _configadd_s.c,
+           _configget.c,
+           _configset.c,
+           _configremove.c,
+           _configtouch.c,
+           _notifyadd.c,
+           _notifyremove.c,
+           _notifychanges.c,
+           _notifyviaport.c,
+           _notifyviafd.c,
+           _notifyviasignal.c,
+           _notifycancel.c,
+           _snapshot.c
+       );
+       OTHER_SOURCES = (
+           Makefile.preamble,
+           Makefile,
+           Makefile.postamble,
+           m.template,
+           h.template,
+           config.defs
+       );
+       PRECOMPILED_HEADERS = ();
+       PROJECT_HEADERS = ();
+       PUBLIC_HEADERS = ();
+       SUBPROJECTS = ();
+    };
+    LANGUAGE = English;
+    MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles";
+    NEXTSTEP_BUILDTOOL = /usr/bin/gnumake;
+    NEXTSTEP_INSTALLDIR = /usr/sbin;
+    NEXTSTEP_JAVA_COMPILER = /usr/bin/javac;
+    NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc;
+    PDO_UNIX_BUILDTOOL = $NEXT_ROOT/Developer/bin/make;
+    PDO_UNIX_INSTALLDIR = /bin;
+    PDO_UNIX_JAVA_COMPILER = "$(JDKBINDIR)/javac";
+    PDO_UNIX_OBJCPLUS_COMPILER = "$(NEXTDEV_BIN)/gcc";
+    PROJECTNAME = configd;
+    PROJECTTYPE = Tool;
+    PROJECTVERSION = 2.8;
+    WINDOWS_BUILDTOOL = $NEXT_ROOT/Developer/Executables/make;
+    WINDOWS_INSTALLDIR = /Library/Executables;
+    WINDOWS_JAVA_COMPILER = "$(JDKBINDIR)/javac.exe";
+    WINDOWS_OBJCPLUS_COMPILER = "$(DEVDIR)/gcc";
+}
diff --git a/configd.tproj/_SCD.c b/configd.tproj/_SCD.c
new file mode 100644 (file)
index 0000000..c89dd8c
--- /dev/null
@@ -0,0 +1,532 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+
+#include "configd.h"
+
+
+CFMutableDictionaryRef sessionData          = NULL;
+
+CFMutableDictionaryRef cacheData            = NULL;
+CFMutableDictionaryRef cacheData_s          = NULL;
+
+CFMutableSetRef                changedKeys          = NULL;
+CFMutableSetRef                changedKeys_s        = NULL;
+
+CFMutableSetRef                deferredRemovals     = NULL;
+CFMutableSetRef                deferredRemovals_s   = NULL;
+
+CFMutableSetRef                removedSessionKeys   = NULL;
+CFMutableSetRef                removedSessionKeys_s = NULL;
+
+CFMutableSetRef                needsNotification    = NULL;
+
+
+void
+_swapLockedCacheData()
+{
+       void    *temp;
+
+       temp                 = cacheData;
+       cacheData            = cacheData_s;
+       cacheData_s          = temp;
+
+       temp                 = changedKeys;
+       changedKeys          = changedKeys_s;
+       changedKeys_s        = temp;
+
+       temp                 = deferredRemovals;
+       deferredRemovals     = deferredRemovals_s;
+       deferredRemovals_s   = temp;
+
+       temp                 = removedSessionKeys;
+       removedSessionKeys   = removedSessionKeys_s;
+       removedSessionKeys_s = temp;
+
+       return;
+}
+
+
+void
+_addWatcher(CFNumberRef sessionNum, CFStringRef watchedKey)
+{
+       CFDictionaryRef         dict;
+       CFMutableDictionaryRef  newDict;
+       CFArrayRef              watchers;
+       CFMutableArrayRef       newWatchers;
+       CFArrayRef              watcherRefs;
+       CFMutableArrayRef       newWatcherRefs;
+       CFIndex                 i;
+       int                     refCnt;
+       CFNumberRef             refNum;
+
+       /*
+        * Get the dictionary associated with this key out of the cache
+        */
+       dict = CFDictionaryGetValue(cacheData, watchedKey);
+       if (dict) {
+               newDict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
+       } else {
+               newDict = CFDictionaryCreateMutable(NULL,
+                                                   0,
+                                                   &kCFTypeDictionaryKeyCallBacks,
+                                                   &kCFTypeDictionaryValueCallBacks);
+       }
+
+       /*
+        * Get the set of watchers out of the keys dictionary
+        */
+       watchers    = CFDictionaryGetValue(newDict, kSCDWatchers);
+       watcherRefs = CFDictionaryGetValue(newDict, kSCDWatcherRefs);
+       if (watchers) {
+               newWatchers    = CFArrayCreateMutableCopy(NULL, 0, watchers);
+               newWatcherRefs = CFArrayCreateMutableCopy(NULL, 0, watcherRefs);
+       } else {
+               newWatchers    = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+               newWatcherRefs = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+       }
+
+       /*
+        * Add my session to the set of watchers
+        */
+       i = CFArrayGetFirstIndexOfValue(newWatchers,
+                                       CFRangeMake(0, CFArrayGetCount(newWatchers)),
+                                       sessionNum);
+       if (i == -1) {
+               /* if this is the first instance of this session watching this key */
+               CFArrayAppendValue(newWatchers, sessionNum);
+               refCnt = 1;
+               refNum = CFNumberCreate(NULL, kCFNumberIntType, &refCnt);
+               CFArrayAppendValue(newWatcherRefs, refNum);
+               CFRelease(refNum);
+       } else {
+               /* if this is another instance of this session watching this key */
+               refNum = CFArrayGetValueAtIndex(newWatcherRefs, i);
+               CFNumberGetValue(refNum, kCFNumberIntType, &refCnt);
+               refCnt++;
+               refNum = CFNumberCreate(NULL, kCFNumberIntType, &refCnt);
+               CFArraySetValueAtIndex(newWatcherRefs, i, refNum);
+               CFRelease(refNum);
+       }
+
+       /*
+        * Update the keys dictionary
+        */
+       CFDictionarySetValue(newDict, kSCDWatchers, newWatchers);
+       CFRelease(newWatchers);
+       CFDictionarySetValue(newDict, kSCDWatcherRefs, newWatcherRefs);
+       CFRelease(newWatcherRefs);
+
+       /*
+        * Update the cache for this key
+        */
+       CFDictionarySetValue(cacheData, watchedKey, newDict);
+       CFRelease(newDict);
+
+       SCDLog(LOG_DEBUG, CFSTR("  _addWatcher: %@, %@"), sessionNum, watchedKey);
+
+       return;
+}
+
+
+/*
+ * _addRegexWatcherByKey()
+ *
+ * This is a CFDictionaryApplierFunction which will iterate over each key
+ * defined in the "cacheData" dictionary. The arguments are the dictionary
+ * key, it's associated cache dictionary, and a context structure which
+ * includes the following:
+ *
+ *   1. the session which has just added a regex notification request
+ *   2. the compiled regular expression associated with the above key.
+ *
+ * If a (real) dictionary key is found which matches the provided regular
+ * expression then we mark that key as being watched by the session.
+ */
+void
+_addRegexWatcherByKey(const void *key, void *val, void *context)
+{
+       CFStringRef     cacheStr  = key;
+       CFDictionaryRef info      = val;
+       mach_port_t     sessionID = ((addContextRef)context)->session->server;
+       regex_t         *preg     = ((addContextRef)context)->preg;
+       int             cacheKeyLen;
+       char            *cacheKey;
+       CFNumberRef     sessionNum;
+       int             reError;
+       char            reErrBuf[256];
+       int             reErrStrLen;
+
+       if (CFDictionaryContainsKey(info, kSCDData) == FALSE) {
+               /* if no data (yet) */
+               return;
+       }
+
+       /* convert cache key to C string */
+       cacheKeyLen = CFStringGetLength(cacheStr) + 1;
+       cacheKey    = CFAllocatorAllocate(NULL, cacheKeyLen, 0);
+       if (!CFStringGetCString(cacheStr, cacheKey, cacheKeyLen, kCFStringEncodingMacRoman)) {
+               SCDLog(LOG_DEBUG, CFSTR("CFStringGetCString: could not convert cache key to C string"));
+               CFAllocatorDeallocate(NULL, cacheKey);
+               return;
+       }
+
+       /* compare cache key to new notification keys regular expression pattern */
+       reError = regexec(preg, cacheKey, 0, NULL, 0);
+       switch (reError) {
+               case 0 :
+                       /* we've got a match */
+                       sessionNum = CFNumberCreate(NULL, kCFNumberIntType, &sessionID);
+                       _addWatcher(sessionNum, cacheStr);
+                       CFRelease(sessionNum);
+                       break;
+               case REG_NOMATCH :
+                       /* no match */
+                       break;
+               default :
+                       reErrStrLen = regerror(reError, preg, reErrBuf, sizeof(reErrBuf));
+                       SCDLog(LOG_DEBUG, CFSTR("regexec(): %s"), reErrBuf);
+                       break;
+       }
+       CFAllocatorDeallocate(NULL, cacheKey);
+}
+
+
+/*
+ * _addRegexWatchersBySession()
+ *
+ * This is a CFDictionaryApplierFunction which will iterate over each session
+ * defined in the "sessionData" dictionary. The arguments are the session
+ * key, it's associated session dictionary, , and the cache key being added.
+ *
+ * If an active session includes any regular expression keys which match the
+ * key being added to the "cacheData" dictionary then we mark this key as being
+ * watched by the session.
+ */
+void
+_addRegexWatchersBySession(const void *key, void *val, void *context)
+{
+       CFStringRef     sessionKey = key;
+       CFDictionaryRef info       = val;
+       CFStringRef     addedKey   = context;
+       CFIndex         newKeyLen;
+       char            *newKeyStr;
+       CFArrayRef      rKeys;
+       CFArrayRef      rData;
+       CFIndex         i;
+
+       if (info == NULL) {
+               /* if no dictionary for this session */
+               return;
+       }
+
+       rKeys = CFDictionaryGetValue(info, kSCDRegexKeys);
+       if (rKeys == NULL) {
+               /* if no regex keys for this session */
+               return;
+       }
+       rData = CFDictionaryGetValue(info, kSCDRegexData);
+
+       /* convert new key to C string */
+       newKeyLen = CFStringGetLength(addedKey) + 1;
+       newKeyStr = CFAllocatorAllocate(NULL, newKeyLen, 0);
+       if (!CFStringGetCString(addedKey, newKeyStr, newKeyLen, kCFStringEncodingMacRoman)) {
+               SCDLog(LOG_DEBUG, CFSTR("CFStringGetCString: could not convert new key to C string"));
+               CFAllocatorDeallocate(NULL, newKeyStr);
+               return;
+       }
+
+       /* iterate over the regex keys looking for an pattern which matches the new key */
+       for (i=0; i<CFArrayGetCount(rKeys); i++) {
+               CFDataRef       regexData = CFArrayGetValueAtIndex(rData, i);
+               regex_t         *preg     = (regex_t *)CFDataGetBytePtr(regexData);
+               int             reError;
+               char            reErrBuf[256];
+               int             reErrStrLen;
+               SInt32          sessionInt;
+               CFNumberRef     sessionNum;
+
+               /* check if this key matches the regular expression */
+               reError = regexec(preg, newKeyStr, 0, NULL, 0);
+               switch (reError) {
+                       case 0 :
+                               /* we've got a match, add a reference */
+                               sessionInt = CFStringGetIntValue(sessionKey);
+                               sessionNum = CFNumberCreate(NULL, kCFNumberIntType, &sessionInt);
+                               _addWatcher(sessionNum, addedKey);
+                               CFRelease(sessionNum);
+                               break;
+                       case REG_NOMATCH :
+                               /* no match */
+                               break;
+                       default :
+                               reErrStrLen = regerror(reError, preg, reErrBuf, sizeof(reErrBuf));
+                               SCDLog(LOG_DEBUG, CFSTR("regexec(): %s"), reErrBuf);
+                               break;
+               }
+
+       }
+       CFAllocatorDeallocate(NULL, newKeyStr);
+
+       return;
+}
+
+
+void
+_removeWatcher(CFNumberRef sessionNum, CFStringRef watchedKey)
+{
+       CFDictionaryRef         dict;
+       CFMutableDictionaryRef  newDict;
+       CFArrayRef              watchers;
+       CFMutableArrayRef       newWatchers;
+       CFArrayRef              watcherRefs;
+       CFMutableArrayRef       newWatcherRefs;
+       CFIndex                 i;
+       int                     refCnt;
+       CFNumberRef             refNum;
+
+       /*
+        * Get the dictionary associated with this key out of the cache
+        */
+       dict = CFDictionaryGetValue(cacheData, watchedKey);
+       if ((dict == NULL) || (CFDictionaryContainsKey(dict, kSCDWatchers) == FALSE)) {
+               /* key doesn't exist (isn't this really fatal?) */
+               SCDLog(LOG_DEBUG, CFSTR("_removeWatcher: key not present in dictionary."));
+               return;
+       }
+       newDict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
+
+       /*
+        * Get the set of watchers out of the keys dictionary and
+        * remove this session from the list.
+        */
+       watchers       = CFDictionaryGetValue(newDict, kSCDWatchers);
+       newWatchers    = CFArrayCreateMutableCopy(NULL, 0, watchers);
+
+       watcherRefs    = CFDictionaryGetValue(newDict, kSCDWatcherRefs);
+       newWatcherRefs = CFArrayCreateMutableCopy(NULL, 0, watcherRefs);
+
+       /* locate the session reference */
+       i = CFArrayGetFirstIndexOfValue(newWatchers,
+                                       CFRangeMake(0, CFArrayGetCount(newWatchers)),
+                                       sessionNum);
+       if (i == -1) {
+               SCDLog(LOG_DEBUG, CFSTR("_removeWatcher: no reference for session %@"), sessionNum);
+               CFRelease(newDict);
+               CFRelease(newWatchers);
+               CFRelease(newWatcherRefs);
+               return;
+       }
+
+       /* remove one session reference */
+       refNum = CFArrayGetValueAtIndex(newWatcherRefs, i);
+       CFNumberGetValue(refNum, kCFNumberIntType, &refCnt);
+       if (--refCnt > 0) {
+               refNum = CFNumberCreate(NULL, kCFNumberIntType, &refCnt);
+               CFArraySetValueAtIndex(newWatcherRefs, i, refNum);
+               CFRelease(refNum);
+       } else {
+               /* if this was the last reference */
+               CFArrayRemoveValueAtIndex(newWatchers, i);
+               CFArrayRemoveValueAtIndex(newWatcherRefs, i);
+       }
+
+       if (CFArrayGetCount(newWatchers) > 0) {
+               /* if this key is still being "watched" */
+               CFDictionarySetValue(newDict, kSCDWatchers, newWatchers);
+               CFDictionarySetValue(newDict, kSCDWatcherRefs, newWatcherRefs);
+       } else {
+               /* no watchers left, remove the empty set */
+               CFDictionaryRemoveValue(newDict, kSCDWatchers);
+               CFDictionaryRemoveValue(newDict, kSCDWatcherRefs);
+       }
+       CFRelease(newWatchers);
+       CFRelease(newWatcherRefs);
+
+       if (CFDictionaryGetCount(newDict) > 0) {
+               /* if this key is still active */
+               CFDictionarySetValue(cacheData, watchedKey, newDict);
+       } else {
+               /* no information left, remove the empty dictionary */
+               CFDictionaryRemoveValue(cacheData, watchedKey);
+       }
+       CFRelease(newDict);
+
+       SCDLog(LOG_DEBUG, CFSTR("  _removeWatcher: %@, %@"), sessionNum, watchedKey);
+
+       return;
+}
+
+
+/*
+ * _removeRegexWatcherByKey()
+ *
+ * This is a CFDictionaryApplierFunction which will iterate over each key
+ * defined in the "cacheData" dictionary. The arguments are the dictionary
+ * key, it's associated cache dictionary, and a context structure which
+ * includes the following:
+ *
+ *   1. the session which has just removed a regex notification request
+ *   2. the compiled regular expression associated with the above key.
+ *
+ * If a key is found and it matches the provided regular expression then
+ * it will its "being watched" status will be cleared.
+ */
+void
+_removeRegexWatcherByKey(const void *key, void *val, void *context)
+{
+       CFStringRef     cacheStr  = key;
+       CFDictionaryRef info      = val;
+       mach_port_t     sessionID = ((removeContextRef)context)->session->server;
+       regex_t         *preg     = ((removeContextRef)context)->preg;
+       CFNumberRef     sessionNum;
+       CFArrayRef      watchers;
+       int             cacheKeyLen;
+       char            *cacheKey;
+       int             reError;
+       char            reErrBuf[256];
+       int             reErrStrLen;
+
+       if ((info == NULL) || (CFDictionaryContainsKey(info, kSCDWatchers) == FALSE)) {
+               /* no dictionary or no watchers */
+               return;
+       }
+
+       sessionNum = CFNumberCreate(NULL, kCFNumberIntType, &sessionID);
+
+       watchers = CFDictionaryGetValue(info, kSCDWatchers);
+       if (CFArrayContainsValue(watchers,
+                                CFRangeMake(0, CFArrayGetCount(watchers)),
+                                sessionNum) == FALSE) {
+               /* this session is not watching this key */
+               CFRelease(sessionNum);
+               return;
+       }
+
+       /* convert key to C string */
+       cacheKeyLen = CFStringGetLength(cacheStr) + 1;
+       cacheKey    = CFAllocatorAllocate(NULL, cacheKeyLen, 0);
+       if (!CFStringGetCString(cacheStr, cacheKey, cacheKeyLen, kCFStringEncodingMacRoman)) {
+               SCDLog(LOG_DEBUG, CFSTR("CFStringGetCString: could not convert key to C string"));
+               CFAllocatorDeallocate(NULL, cacheKey);
+               CFRelease(sessionNum);
+               return;
+       }
+
+       /* check if this key matches the regular expression */
+       reError = regexec(preg, cacheKey, 0, NULL, 0);
+       switch (reError) {
+               case 0 :
+                       /* we've got a match */
+                       _removeWatcher(sessionNum, cacheStr);
+                       break;
+               case REG_NOMATCH :
+                       /* no match */
+                       break;
+               default :
+                       reErrStrLen = regerror(reError, preg, reErrBuf, sizeof(reErrBuf));
+                       SCDLog(LOG_DEBUG, CFSTR("regexec(): %s"), reErrBuf);
+                       break;
+       }
+       CFAllocatorDeallocate(NULL, cacheKey);
+       CFRelease(sessionNum);
+}
+
+
+/*
+ * _removeRegexWatchersBySession()
+ *
+ * This is a CFDictionaryApplierFunction which will iterate over each session
+ * defined in the "sessionData" dictionary. The arguments are the session
+ * key, it's associated session dictionary, and the cache key being removed.
+ *
+ * If an active session includes any regular expression keys which match the
+ * key being removed from the "cacheData" dictionary then we clear this keys
+ * reference of being watched.
+ */
+void
+_removeRegexWatchersBySession(const void *key, void *val, void *context)
+{
+       CFStringRef     sessionKey = key;
+       CFDictionaryRef info       = val;
+       CFStringRef     removedKey = context;
+       CFIndex         oldKeyLen;
+       char            *oldKeyStr;
+       CFArrayRef      rKeys;
+       CFArrayRef      rData;
+       CFIndex         i;
+
+       if (info == NULL) {
+               /* if no dictionary for this session */
+               return;
+       }
+
+       rKeys = CFDictionaryGetValue(info, kSCDRegexKeys);
+       if (rKeys == NULL) {
+               /* if no regex keys for this session */
+               return;
+       }
+       rData = CFDictionaryGetValue(info, kSCDRegexData);
+
+       /* convert new key to C string */
+       oldKeyLen = CFStringGetLength(removedKey) + 1;
+       oldKeyStr = CFAllocatorAllocate(NULL, oldKeyLen, 0);
+       if (!CFStringGetCString(removedKey, oldKeyStr, oldKeyLen, kCFStringEncodingMacRoman)) {
+               SCDLog(LOG_DEBUG, CFSTR("CFStringGetCString: could not convert old key to C string"));
+               CFAllocatorDeallocate(NULL, oldKeyStr);
+               return;
+       }
+
+       /* iterate over the regex keys looking for an pattern which matches the old key */
+       for (i=0; i<CFArrayGetCount(rKeys); i++) {
+               CFDataRef       regexData = CFArrayGetValueAtIndex(rData, i);
+               regex_t         *preg     = (regex_t *)CFDataGetBytePtr(regexData);
+               int             reError;
+               char            reErrBuf[256];
+               int             reErrStrLen;
+               SInt32          sessionInt;
+               CFNumberRef     sessionNum;
+
+               /* check if this key matches the regular expression */
+               reError = regexec(preg, oldKeyStr, 0, NULL, 0);
+               switch (reError) {
+                       case 0 :
+                               /* we've got a match, remove a reference */
+                               sessionInt = CFStringGetIntValue(sessionKey);
+                               sessionNum = CFNumberCreate(NULL, kCFNumberIntType, &sessionInt);
+                               _removeWatcher(sessionNum, removedKey);
+                               CFRelease(sessionNum);
+                               break;
+                       case REG_NOMATCH :
+                               /* no match */
+                               break;
+                       default :
+                               reErrStrLen = regerror(reError, preg, reErrBuf, sizeof(reErrBuf));
+                               SCDLog(LOG_DEBUG, CFSTR("regexec(): %s"), reErrBuf);
+                               break;
+               }
+
+       }
+       CFAllocatorDeallocate(NULL, oldKeyStr);
+
+       return;
+}
diff --git a/configd.tproj/_SCD.h b/configd.tproj/_SCD.h
new file mode 100644 (file)
index 0000000..32cf319
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+
+#ifndef _S_SCD_H
+#define _S_SCD_H
+
+#include <sys/cdefs.h>
+
+
+/*
+ * keys in the "cacheData" dictionary
+ */
+
+/*
+ * data associated with a key
+ */
+#define        kSCDData        CFSTR("data")
+/*
+ * instance value associated with a key
+ */
+#define        kSCDInstance    CFSTR("instance")
+/*
+ * client session ids watching a key and, since we can possibly have
+ * multiple regex keys which reference the key, a count of active
+ * references
+ */
+#define        kSCDWatchers    CFSTR("watchers")
+#define        kSCDWatcherRefs CFSTR("watcherRefs")
+/*
+ * client session id for per-session keys.
+ */
+#define        kSCDSession     CFSTR("session")
+
+
+/*
+ * keys in the "sessionData" dictionary
+ */
+
+/*
+ * the name of the calling application / plug-in
+ */
+#define        kSCDName        CFSTR("name")
+/*
+ * keys which have changed since last call to SCDNotifierGetChanges()
+ */
+#define        kSCDChangedKeys CFSTR("changedKeys")
+/*
+ * for notification keys which consist of a regular expression we keep
+ * both the pattern string and the compiled regular expression
+ */
+#define        kSCDRegexKeys   CFSTR("regexKeys")
+#define        kSCDRegexData   CFSTR("regexData")
+/*
+ * keys which are to be removed when the session is closed
+ */
+#define        kSCDSessionKeys CFSTR("sessionKeys")
+
+
+extern CFMutableDictionaryRef  cacheData;
+extern CFMutableDictionaryRef  sessionData;
+extern CFMutableSetRef         changedKeys;
+extern CFMutableSetRef         deferredRemovals;
+extern CFMutableSetRef         removedSessionKeys;
+extern CFMutableSetRef         needsNotification;
+
+extern CFMutableDictionaryRef  cacheData_s;
+extern CFMutableSetRef         changedKeys_s;
+extern CFMutableSetRef         deferredRemovals_s;
+extern CFMutableSetRef         removedSessionKeys_s;
+
+/*
+ * "context" argument for CFDictionaryArrayApplier _addRegexWatcherByKey(),
+ * _addRegexWatcherBySession(), and _removeRegexWatcherByKey() functions.
+ */
+typedef struct {
+       SCDSessionPrivateRef    session;
+       regex_t                 *preg;
+} addContext, *addContextRef;
+
+
+typedef struct {
+       SCDSessionPrivateRef    session;
+       regex_t                 *preg;
+} removeContext, *removeContextRef;
+
+
+__BEGIN_DECLS
+
+SCDStatus      _SCDOpen                        __P((SCDSessionRef      *session,
+                                                    CFStringRef        name));
+
+SCDStatus      _SCDClose                       __P((SCDSessionRef      *session));
+
+SCDStatus      _SCDLock                        __P((SCDSessionRef      session));
+
+SCDStatus      _SCDUnlock                      __P((SCDSessionRef      session));
+
+SCDStatus      _SCDList                        __P((SCDSessionRef      session,
+                                                    CFStringRef        key,
+                                                    int                regexOptions,
+                                                    CFArrayRef         *subKeys));
+
+SCDStatus      _SCDAdd                         __P((SCDSessionRef      session,
+                                                    CFStringRef        key,
+                                                    SCDHandleRef       handle));
+
+SCDStatus      _SCDAddSession                  __P((SCDSessionRef      session,
+                                                    CFStringRef        key,
+                                                    SCDHandleRef       handle));
+
+SCDStatus      _SCDGet                         __P((SCDSessionRef      session,
+                                                    CFStringRef        key,
+                                                    SCDHandleRef       *handle));
+
+SCDStatus      _SCDSet                         __P((SCDSessionRef      session,
+                                                    CFStringRef        key,
+                                                    SCDHandleRef       handle));
+
+SCDStatus      _SCDRemove                      __P((SCDSessionRef      session,
+                                                    CFStringRef        key));
+
+SCDStatus      _SCDTouch                       __P((SCDSessionRef      session,
+                                                    CFStringRef        key));
+
+SCDStatus      _SCDSnapshot                    __P((SCDSessionRef      session));
+
+SCDStatus      _SCDNotifierList                __P((SCDSessionRef      session,
+                                                    int                regexOptions,
+                                                    CFArrayRef         *notifierKeys));
+
+SCDStatus      _SCDNotifierAdd                 __P((SCDSessionRef      session,
+                                                    CFStringRef        key,
+                                                    int                regexOptions));
+
+SCDStatus      _SCDNotifierRemove              __P((SCDSessionRef      session,
+                                                    CFStringRef        key,
+                                                    int                regexOptions));
+
+SCDStatus      _SCDNotifierGetChanges          __P((SCDSessionRef      session,
+                                                    CFArrayRef         *notifierKeys));
+
+SCDStatus      _SCDNotifierInformViaMachPort   __P((SCDSessionRef      session,
+                                                    mach_msg_id_t      msgid,
+                                                    mach_port_t        *port));
+
+SCDStatus      _SCDNotifierInformViaFD         __P((SCDSessionRef      session,
+                                                    int32_t            identifier,
+                                                    int                *fd));
+
+SCDStatus      _SCDNotifierInformViaSignal     __P((SCDSessionRef      session,
+                                                    pid_t              pid,
+                                                    int                sig));
+
+SCDStatus      _SCDNotifierCancel              __P((SCDSessionRef      session));
+
+void           _swapLockedCacheData            __P(());
+
+void           _addWatcher                     __P((CFNumberRef        sessionNum,
+                                                    CFStringRef        watchedKey));
+
+void           _addRegexWatcherByKey           __P((const void         *key,
+                                                    void               *val,
+                                                    void               *context));
+
+void           _addRegexWatchersBySession      __P((const void         *key,
+                                                    void               *val,
+                                                    void               *context));
+
+void           _removeWatcher                  __P((CFNumberRef        sessionNum,
+                                                    CFStringRef        watchedKey));
+
+void           _removeRegexWatcherByKey        __P((const void         *key,
+                                                    void               *val,
+                                                    void               *context));
+
+void           _removeRegexWatchersBySession   __P((const void         *key,
+                                                    void               *val,
+                                                    void               *context));
+
+__END_DECLS
+
+#endif /* !_S_SCD_H */
diff --git a/configd.tproj/_configadd.c b/configd.tproj/_configadd.c
new file mode 100644 (file)
index 0000000..76a9dfb
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include "configd.h"
+#include "session.h"
+
+SCDStatus
+_SCDAdd(SCDSessionRef session, CFStringRef key, SCDHandleRef handle)
+{
+       SCDSessionPrivateRef    sessionPrivate = (SCDSessionPrivateRef)session;
+       SCDStatus               scd_status = SCD_OK;
+       boolean_t               wasLocked;
+       SCDHandleRef            tempHandle;
+
+       SCDLog(LOG_DEBUG, CFSTR("_SCDAdd:"));
+       SCDLog(LOG_DEBUG, CFSTR("  key          = %@"), key);
+       SCDLog(LOG_DEBUG, CFSTR("  data         = %@"), SCDHandleGetData(handle));
+
+       if ((session == NULL) || (sessionPrivate->server == MACH_PORT_NULL)) {
+               return SCD_NOSESSION;           /* you can't do anything with a closed session */
+       }
+
+       /*
+        * 1. Determine if the cache lock is currently held
+        *    and acquire the lock if necessary.
+        */
+       wasLocked = SCDOptionGet(NULL, kSCDOptionIsLocked);
+       if (!wasLocked) {
+               scd_status = _SCDLock(session);
+               if (scd_status != SCD_OK) {
+                       SCDLog(LOG_DEBUG, CFSTR("  _SCDLock(): %s"), SCDError(scd_status));
+                       return scd_status;
+               }
+       }
+
+       /*
+        * 2. Ensure that this is a new key.
+        */
+       scd_status = _SCDGet(session, key, &tempHandle);
+       switch (scd_status) {
+               case SCD_NOKEY :
+                       /* cache key does not exist, proceed */
+                       break;
+
+               case SCD_OK :
+                       /* cache key exists, sorry */
+                       SCDHandleRelease(tempHandle);
+                       scd_status = SCD_EXISTS;
+                       goto done;
+
+               default :
+                       SCDLog(LOG_DEBUG, CFSTR("  _SCDGet(): %s"), SCDError(scd_status));
+                       goto done;
+       }
+
+       /*
+        * 3. Save the new key.
+        */
+       scd_status = _SCDSet(session, key, handle);
+
+       /*
+        * 4. Release the lock if we acquired it as part of this request.
+        */
+    done:
+       if (!wasLocked)
+               _SCDUnlock(session);
+
+       return scd_status;
+}
+
+
+kern_return_t
+_configadd(mach_port_t                         server,
+          xmlData_t                    keyRef,         /* raw XML bytes */
+          mach_msg_type_number_t       keyLen,
+          xmlData_t                    dataRef,        /* raw XML bytes */
+          mach_msg_type_number_t       dataLen,
+          int                          *newInstance,
+          int                          *scd_status
+)
+{
+       kern_return_t           status;
+       serverSessionRef        mySession = getSession(server);
+       CFDataRef               xmlKey;         /* key  (XML serialized) */
+       CFStringRef             key;            /* key  (un-serialized) */
+       CFDataRef               xmlData;        /* data (XML serialized) */
+       CFPropertyListRef       data;           /* data (un-serialized) */
+       SCDHandleRef            handle;
+       CFStringRef             xmlError;
+
+       SCDLog(LOG_DEBUG, CFSTR("Add key to configuration database."));
+       SCDLog(LOG_DEBUG, CFSTR("  server = %d"), server);
+
+       /* un-serialize the key */
+       xmlKey = CFDataCreate(NULL, keyRef, keyLen);
+       status = vm_deallocate(mach_task_self(), (vm_address_t)keyRef, keyLen);
+       if (status != KERN_SUCCESS) {
+               SCDLog(LOG_DEBUG, CFSTR("vm_deallocate(): %s"), mach_error_string(status));
+               /* non-fatal???, proceed */
+       }
+       key = CFPropertyListCreateFromXMLData(NULL,
+                                             xmlKey,
+                                             kCFPropertyListImmutable,
+                                             &xmlError);
+       CFRelease(xmlKey);
+       if (xmlError) {
+               SCDLog(LOG_DEBUG, CFSTR("CFPropertyListCreateFromXMLData() key: %s"), xmlError);
+               *scd_status = SCD_FAILED;
+               return KERN_SUCCESS;
+       }
+
+       /* un-serialize the data */
+       xmlData = CFDataCreate(NULL, dataRef, dataLen);
+       status = vm_deallocate(mach_task_self(), (vm_address_t)dataRef, dataLen);
+       if (status != KERN_SUCCESS) {
+               SCDLog(LOG_DEBUG, CFSTR("vm_deallocate(): %s"), mach_error_string(status));
+               /* non-fatal???, proceed */
+       }
+       data = CFPropertyListCreateFromXMLData(NULL,
+                                              xmlData,
+                                              kCFPropertyListImmutable,
+                                              &xmlError);
+       CFRelease(xmlData);
+       if (xmlError) {
+               SCDLog(LOG_DEBUG, CFSTR("CFPropertyListCreateFromXMLData() data: %s"), xmlError);
+               CFRelease(key);
+               *scd_status = SCD_FAILED;
+               return KERN_SUCCESS;
+       }
+
+       handle = SCDHandleInit();
+       SCDHandleSetData(handle, data);
+       *scd_status = _SCDAdd(mySession->session, key, handle);
+       if (*scd_status == SCD_OK) {
+               *newInstance = SCDHandleGetInstance(handle);
+       }
+       SCDHandleRelease(handle);
+       CFRelease(key);
+       CFRelease(data);
+
+       return KERN_SUCCESS;
+}
diff --git a/configd.tproj/_configadd_s.c b/configd.tproj/_configadd_s.c
new file mode 100644 (file)
index 0000000..8e54070
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include "configd.h"
+#include "session.h"
+
+SCDStatus
+_SCDAddSession(SCDSessionRef session, CFStringRef key, SCDHandleRef handle)
+{
+       SCDSessionPrivateRef    sessionPrivate = (SCDSessionPrivateRef)session;
+       SCDStatus               scd_status = SCD_OK;
+       CFStringRef             sessionKey;
+       CFDictionaryRef         dict;
+       CFMutableDictionaryRef  newDict;
+       CFArrayRef              keys;
+       CFMutableArrayRef       newKeys;
+
+       SCDLog(LOG_DEBUG, CFSTR("_SCDAddSession:"));
+       SCDLog(LOG_DEBUG, CFSTR("  key          = %@"), key);
+       SCDLog(LOG_DEBUG, CFSTR("  data         = %@"), SCDHandleGetData(handle));
+
+       if ((session == NULL) || (sessionPrivate->server == MACH_PORT_NULL)) {
+               return SCD_NOSESSION;           /* you can't do anything with a closed session */
+       }
+
+       /*
+        * 1. Add the key
+        */
+       scd_status = _SCDAdd(session, key, handle);
+       if (scd_status != SCD_OK) {
+               SCDLog(LOG_DEBUG, CFSTR("  _SCDAdd(): %s"), SCDError(scd_status));
+               return scd_status;
+       }
+
+       /*
+        * 2. Create the session key
+        */
+       sessionKey = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), sessionPrivate->server);
+
+       /*
+        * 3. Add this key to my list of per-session keys
+        */
+       dict = CFDictionaryGetValue(sessionData, sessionKey);
+       keys = CFDictionaryGetValue(dict, kSCDSessionKeys);
+       if ((keys == NULL) ||
+           (CFArrayGetFirstIndexOfValue(keys,
+                                        CFRangeMake(0, CFArrayGetCount(keys)),
+                                        key) == -1)) {
+               /*
+                * if no session keys defined "or" keys defined but not
+                * this one...
+                */
+               if (keys) {
+                       /* this is a new session key */
+                       newKeys = CFArrayCreateMutableCopy(NULL, 0, keys);
+               } else {
+                       /* this is an additional session key */
+                       newKeys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+               }
+               CFArrayAppendValue(newKeys, key);
+
+               /* update session dictionary */
+               newDict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
+               CFDictionarySetValue(newDict, kSCDSessionKeys, newKeys);
+               CFRelease(newKeys);
+               CFDictionarySetValue(sessionData, sessionKey, newDict);
+               CFRelease(newDict);
+       }
+
+       /*
+        * 4. Mark the key as a "session" key and track the creator.
+        */
+       dict    = CFDictionaryGetValue(cacheData, key);
+       newDict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
+       CFDictionarySetValue(newDict, kSCDSession, sessionKey);
+       CFDictionarySetValue(cacheData, key, newDict);
+       CFRelease(newDict);
+
+       CFRelease(sessionKey);
+       return scd_status;
+}
+
+
+kern_return_t
+_configadd_s(mach_port_t               server,
+            xmlData_t                  keyRef,         /* raw XML bytes */
+            mach_msg_type_number_t     keyLen,
+            xmlData_t                  dataRef,        /* raw XML bytes */
+            mach_msg_type_number_t     dataLen,
+            int                        *newInstance,
+            int                        *scd_status
+)
+{
+       kern_return_t           status;
+       serverSessionRef        mySession = getSession(server);
+       CFDataRef               xmlKey;         /* key  (XML serialized) */
+       CFStringRef             key;            /* key  (un-serialized) */
+       CFDataRef               xmlData;        /* data (XML serialized) */
+       CFPropertyListRef       data;           /* data (un-serialized) */
+       SCDHandleRef            handle;
+       CFStringRef             xmlError;
+
+       SCDLog(LOG_DEBUG, CFSTR("Add (session) key to configuration database."));
+       SCDLog(LOG_DEBUG, CFSTR("  server = %d"), server);
+
+       /* un-serialize the key */
+       xmlKey = CFDataCreate(NULL, keyRef, keyLen);
+       status = vm_deallocate(mach_task_self(), (vm_address_t)keyRef, keyLen);
+       if (status != KERN_SUCCESS) {
+               SCDLog(LOG_DEBUG, CFSTR("vm_deallocate(): %s"), mach_error_string(status));
+               /* non-fatal???, proceed */
+       }
+       key = CFPropertyListCreateFromXMLData(NULL,
+                                             xmlKey,
+                                             kCFPropertyListImmutable,
+                                             &xmlError);
+       CFRelease(xmlKey);
+       if (xmlError) {
+               SCDLog(LOG_DEBUG, CFSTR("CFPropertyListCreateFromXMLData() key: %s"), xmlError);
+               *scd_status = SCD_FAILED;
+               return KERN_SUCCESS;
+       }
+
+       /* un-serialize the data */
+       xmlData = CFDataCreate(NULL, dataRef, dataLen);
+       status = vm_deallocate(mach_task_self(), (vm_address_t)dataRef, dataLen);
+       if (status != KERN_SUCCESS) {
+               SCDLog(LOG_DEBUG, CFSTR("vm_deallocate(): %s"), mach_error_string(status));
+               /* non-fatal???, proceed */
+       }
+       data = CFPropertyListCreateFromXMLData(NULL,
+                                              xmlData,
+                                              kCFPropertyListImmutable,
+                                              &xmlError);
+       CFRelease(xmlData);
+       if (xmlError) {
+               SCDLog(LOG_DEBUG, CFSTR("CFPropertyListCreateFromXMLData() data: %s"), xmlError);
+               CFRelease(key);
+               *scd_status = SCD_FAILED;
+               return KERN_SUCCESS;
+       }
+
+       handle = SCDHandleInit();
+       SCDHandleSetData(handle, data);
+       *scd_status = _SCDAddSession(mySession->session, key, handle);
+       if (*scd_status == SCD_OK) {
+               *newInstance = SCDHandleGetInstance(handle);
+       }
+       SCDHandleRelease(handle);
+       CFRelease(key);
+       CFRelease(data);
+
+       return KERN_SUCCESS;
+}
diff --git a/configd.tproj/_configclose.c b/configd.tproj/_configclose.c
new file mode 100644 (file)
index 0000000..afba5bb
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include <unistd.h>
+
+#include "configd.h"
+#include "session.h"
+
+static boolean_t
+isMySessionKey(CFStringRef sessionKey, CFStringRef key)
+{
+       CFDictionaryRef dict;
+       CFStringRef     cacheSessionKey;
+
+       dict = CFDictionaryGetValue(cacheData, key);
+       if (!dict) {
+               /* if key no longer exists */
+               return FALSE;
+       }
+
+       cacheSessionKey = CFDictionaryGetValue(dict, kSCDSession);
+       if (!cacheSessionKey) {
+               /* if this is not a session key */
+               return FALSE;
+       }
+
+       if (!CFEqual(sessionKey, cacheSessionKey)) {
+               /* if this is not "my" session key */
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+
+SCDStatus
+_SCDClose(SCDSessionRef *session)
+{
+       SCDSessionPrivateRef    sessionPrivate = (SCDSessionPrivateRef)*session;
+       CFIndex                 keyCnt;
+       CFStringRef             sessionKey;
+       CFDictionaryRef         dict;
+       CFArrayRef              keys;
+       serverSessionRef        mySession;
+
+       SCDLog(LOG_DEBUG, CFSTR("_SCDClose:"));
+
+       if ((*session == NULL) || (sessionPrivate->server == MACH_PORT_NULL)) {
+               return SCD_NOSESSION;
+       }
+
+       /* Remove notification keys */
+       if ((keyCnt = CFSetGetCount(sessionPrivate->keys)) > 0) {
+               void            **watchedKeys;
+               CFArrayRef      keysToRemove;
+               CFIndex         i;
+
+               watchedKeys = CFAllocatorAllocate(NULL, keyCnt * sizeof(CFStringRef), 0);
+               CFSetGetValues(sessionPrivate->keys, watchedKeys);
+               keysToRemove = CFArrayCreate(NULL, watchedKeys, keyCnt, &kCFTypeArrayCallBacks);
+               CFAllocatorDeallocate(NULL, watchedKeys);
+               for (i=0; i<keyCnt; i++) {
+                       (void) _SCDNotifierRemove(*session,
+                                                 CFArrayGetValueAtIndex(keysToRemove, i),
+                                                 0);
+               }
+               CFRelease(keysToRemove);
+       }
+
+       /* Remove regex notification keys */
+       if ((keyCnt = CFSetGetCount(sessionPrivate->reKeys)) > 0) {
+               void            **watchedKeys;
+               CFArrayRef      keysToRemove;
+               CFIndex         i;
+
+               watchedKeys = CFAllocatorAllocate(NULL, keyCnt * sizeof(CFStringRef), 0);
+               CFSetGetValues(sessionPrivate->reKeys, watchedKeys);
+               keysToRemove = CFArrayCreate(NULL, watchedKeys, keyCnt, &kCFTypeArrayCallBacks);
+               CFAllocatorDeallocate(NULL, watchedKeys);
+               for (i=0; i<keyCnt; i++) {
+                       (void) _SCDNotifierRemove(*session,
+                                                 CFArrayGetValueAtIndex(keysToRemove, i),
+                                                 kSCDRegexKey);
+               }
+               CFRelease(keysToRemove);
+       }
+
+       /* Remove/cancel any outstanding notification requests. */
+       (void) _SCDNotifierCancel(*session);
+
+       /* Remove any session keys */
+       sessionKey = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), sessionPrivate->server);
+       dict = CFDictionaryGetValue(sessionData, sessionKey);
+       keys = CFDictionaryGetValue(dict, kSCDSessionKeys);
+       if (keys && ((keyCnt = CFArrayGetCount(keys)) > 0)) {
+               boolean_t       wasLocked;
+               CFIndex         i;
+
+               /*
+                * if necessary, claim a lock to ensure that we inform
+                * any processes that a session key was removed.
+                */
+               wasLocked = SCDOptionGet(NULL, kSCDOptionIsLocked);
+               if (!wasLocked) {
+                       (void) _SCDLock(*session);
+               }
+
+               /* remove keys from "locked" cache" */
+               for (i=0; i<keyCnt; i++) {
+                       if (isMySessionKey(sessionKey, CFArrayGetValueAtIndex(keys, i)))
+                               (void) _SCDRemove(*session, CFArrayGetValueAtIndex(keys, i));
+               }
+
+               if (wasLocked) {
+                       /* remove keys from "unlocked" cache" */
+                       _swapLockedCacheData();
+                       for (i=0; i<keyCnt; i++) {
+                               if (isMySessionKey(sessionKey, CFArrayGetValueAtIndex(keys, i)))
+                                       (void) _SCDRemove(*session, CFArrayGetValueAtIndex(keys, i));
+                       }
+                       _swapLockedCacheData();
+               }
+
+               /*
+                * Note: everyone who calls _SCDClose() ends up
+                *       removing this sessions dictionary. As
+                *       such, we don't need to worry about
+                *       the session keys.
+                */
+       }
+       CFRelease(sessionKey);
+
+       /* release the lock */
+       if (SCDOptionGet(*session, kSCDOptionIsLocked)) {
+               (void) _SCDUnlock(*session);
+       }
+
+       /*
+        * Invalidate the server port (for this client) which will result
+        * in the removal of any associated run loop sources.
+        */
+       mySession = getSession(sessionPrivate->server);
+       if (mySession->serverRunLoopSource) {
+               CFRunLoopRemoveSource(CFRunLoopGetCurrent(),
+                                     mySession->serverRunLoopSource,
+                                     kCFRunLoopDefaultMode);
+               CFRelease(mySession->serverRunLoopSource);
+       }
+       CFMachPortInvalidate(mySession->serverPort);
+       CFRelease(mySession->serverPort);
+
+       CFRelease(sessionPrivate->keys);
+       CFRelease(sessionPrivate->reKeys);
+       CFAllocatorDeallocate(NULL, sessionPrivate);
+       *session = NULL;
+
+       return SCD_OK;
+}
+
+
+kern_return_t
+_configclose(mach_port_t server, int *scd_status)
+{
+       serverSessionRef        mySession = getSession(server);
+
+       SCDLog(LOG_DEBUG, CFSTR("Close session."));
+       SCDLog(LOG_DEBUG, CFSTR("  server = %d"), server);
+
+       /*
+        * Close the session.
+        */
+       *scd_status = _SCDClose(&mySession->session);
+       if (*scd_status != SCD_OK) {
+               SCDLog(LOG_DEBUG, CFSTR("  _SCDClose(): %s"), SCDError(*scd_status));
+               return KERN_SUCCESS;
+       }
+
+       /*
+        * Remove the session entry.
+        */
+       removeSession(server);
+
+       return KERN_SUCCESS;
+}
diff --git a/configd.tproj/_configget.c b/configd.tproj/_configget.c
new file mode 100644 (file)
index 0000000..7b1e64d
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include "configd.h"
+#include "session.h"
+
+SCDStatus
+_SCDGet(SCDSessionRef session, CFStringRef key, SCDHandleRef *handle)
+{
+       SCDSessionPrivateRef    sessionPrivate = (SCDSessionPrivateRef)session;
+       CFDictionaryRef         dict;
+       CFNumberRef             num;
+       int                     dictInstance;
+
+       SCDLog(LOG_DEBUG, CFSTR("_SCDGet:"));
+       SCDLog(LOG_DEBUG, CFSTR("  key      = %@"), key);
+
+       if ((session == NULL) || (sessionPrivate->server == MACH_PORT_NULL)) {
+               return SCD_NOSESSION;
+       }
+
+       dict = CFDictionaryGetValue(cacheData, key);
+       if ((dict == NULL) || (CFDictionaryContainsKey(dict, kSCDData) == FALSE)) {
+               /* key doesn't exist (or data never defined) */
+               return SCD_NOKEY;
+       }
+
+       /* Create a new handle associated with the cached data */
+       *handle = SCDHandleInit();
+
+       /* Return the data associated with the key */
+       SCDHandleSetData(*handle, CFDictionaryGetValue(dict, kSCDData));
+
+       /* Return the instance number associated with the key */
+       num = CFDictionaryGetValue(dict, kSCDInstance);
+       (void) CFNumberGetValue(num, kCFNumberIntType, &dictInstance);
+       _SCDHandleSetInstance(*handle, dictInstance);
+
+       SCDLog(LOG_DEBUG, CFSTR("  data     = %@"), SCDHandleGetData(*handle));
+       SCDLog(LOG_DEBUG, CFSTR("  instance = %d"), SCDHandleGetInstance(*handle));
+
+       return SCD_OK;
+}
+
+
+kern_return_t
+_configget(mach_port_t                 server,
+          xmlData_t                    keyRef,         /* raw XML bytes */
+          mach_msg_type_number_t       keyLen,
+          xmlDataOut_t                 *dataRef,       /* raw XML bytes */
+          mach_msg_type_number_t       *dataLen,
+          int                          *newInstance,
+          int                          *scd_status
+)
+{
+       kern_return_t           status;
+       serverSessionRef        mySession = getSession(server);
+       CFDataRef               xmlKey;         /* key  (XML serialized) */
+       CFStringRef             key;            /* key  (un-serialized) */
+       CFDataRef               xmlData;        /* data (XML serialized) */
+       SCDHandleRef            handle;
+       CFStringRef             xmlError;
+
+       SCDLog(LOG_DEBUG, CFSTR("Get key from configuration database."));
+       SCDLog(LOG_DEBUG, CFSTR("  server = %d"), server);
+
+       /* un-serialize the key */
+       xmlKey = CFDataCreate(NULL, keyRef, keyLen);
+       status = vm_deallocate(mach_task_self(), (vm_address_t)keyRef, keyLen);
+       if (status != KERN_SUCCESS) {
+               SCDLog(LOG_DEBUG, CFSTR("vm_deallocate(): %s"), mach_error_string(status));
+               /* non-fatal???, proceed */
+       }
+       key = CFPropertyListCreateFromXMLData(NULL,
+                                             xmlKey,
+                                             kCFPropertyListImmutable,
+                                             &xmlError);
+       CFRelease(xmlKey);
+       if (xmlError) {
+               SCDLog(LOG_DEBUG, CFSTR("CFPropertyListCreateFromXMLData() key: %s"), xmlError);
+               *scd_status = SCD_FAILED;
+               return KERN_SUCCESS;
+       }
+
+       *scd_status = _SCDGet(mySession->session, key, &handle);
+       CFRelease(key);
+       if (*scd_status != SCD_OK) {
+               *dataRef = NULL;
+               *dataLen = 0;
+               return KERN_SUCCESS;
+       }
+
+       /*
+        * serialize the data, copy it into an allocated buffer which will be
+        * released when it is returned as part of a Mach message.
+        */
+       xmlData = CFPropertyListCreateXMLData(NULL, SCDHandleGetData(handle));
+       *dataLen = CFDataGetLength(xmlData);
+       status = vm_allocate(mach_task_self(), (void *)dataRef, *dataLen, TRUE);
+       if (status != KERN_SUCCESS) {
+               SCDLog(LOG_DEBUG, CFSTR("vm_allocate(): %s"), mach_error_string(status));
+               *scd_status = SCD_FAILED;
+               CFRelease(xmlData);
+               *dataRef = NULL;
+               *dataLen = 0;
+               return KERN_SUCCESS;
+       }
+
+       bcopy((char *)CFDataGetBytePtr(xmlData), *dataRef, *dataLen);
+       CFRelease(xmlData);
+
+       /*
+        * return the instance number associated with the returned data.
+        */
+       *newInstance = SCDHandleGetInstance(handle);
+
+       SCDHandleRelease(handle);
+
+       return KERN_SUCCESS;
+}
diff --git a/configd.tproj/_configlist.c b/configd.tproj/_configlist.c
new file mode 100644 (file)
index 0000000..85286c3
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include "configd.h"
+#include "session.h"
+
+SCDStatus
+_SCDList(SCDSessionRef session, CFStringRef key, int regexOptions, CFArrayRef *subKeys)
+{
+       SCDSessionPrivateRef    sessionPrivate = (SCDSessionPrivateRef)session;
+       CFIndex                 cacheCnt;
+       void                    **cacheKeys;
+       void                    **cacheValues;
+       CFMutableArrayRef       keyArray;
+       int                     i;
+       CFStringRef             cacheStr;
+       CFDictionaryRef         cacheValue;
+       int                     regexStrLen;
+       char                    *regexStr = NULL;
+       regex_t                 preg;
+       int                     reError;
+       char                    reErrBuf[256];
+       int                     reErrStrLen;
+
+       SCDLog(LOG_DEBUG, CFSTR("_SCDList:"));
+       SCDLog(LOG_DEBUG, CFSTR("  key          = %@"), key);
+       SCDLog(LOG_DEBUG, CFSTR("  regexOptions = %0o"), regexOptions);
+
+       if ((session == NULL) || (sessionPrivate->server == MACH_PORT_NULL)) {
+               return SCD_NOSESSION;
+       }
+
+       cacheCnt = CFDictionaryGetCount(cacheData);
+       keyArray = CFArrayCreateMutable(NULL, cacheCnt, &kCFTypeArrayCallBacks);
+
+       if (regexOptions & kSCDRegexKey) {
+               /*
+                * compile the provided regular expression using the
+                * provided regexOptions (passing only those flags
+                * which would make sense).
+                */
+               regexStrLen = CFStringGetLength(key) + 1;
+               regexStr    = CFAllocatorAllocate(NULL, regexStrLen, 0);
+               if (!CFStringGetCString(key,
+                                       regexStr,
+                                       regexStrLen,
+                                       kCFStringEncodingMacRoman)) {
+                       SCDLog(LOG_DEBUG, CFSTR("CFStringGetCString() key: could not convert to regex string"));
+                       CFAllocatorDeallocate(NULL, regexStr);
+                       return SCD_FAILED;
+               }
+
+               reError = regcomp(&preg, regexStr, REG_EXTENDED);
+               if (reError != 0) {
+                       reErrStrLen = regerror(reError, &preg, reErrBuf, sizeof(reErrBuf));
+                       cacheStr = CFStringCreateWithCString(NULL, reErrBuf, kCFStringEncodingMacRoman);
+                       CFArrayAppendValue(keyArray, cacheStr);
+                       CFRelease(cacheStr);
+                       *subKeys = CFArrayCreateCopy(NULL, keyArray);
+                       CFRelease(keyArray);
+                       CFAllocatorDeallocate(NULL, regexStr);
+                       return SCD_FAILED;
+               }
+       }
+
+       cacheKeys   = CFAllocatorAllocate(NULL, cacheCnt * sizeof(CFStringRef), 0);
+       cacheValues = CFAllocatorAllocate(NULL, cacheCnt * sizeof(CFStringRef), 0);
+       CFDictionaryGetKeysAndValues(cacheData, cacheKeys, cacheValues);
+       for (i=0; i<cacheCnt; i++) {
+               cacheStr   = (CFStringRef)cacheKeys[i];
+               cacheValue = (CFDictionaryRef)cacheValues[i];
+               if (regexOptions & kSCDRegexKey) {
+                       /*
+                        * only return those keys which match the regular
+                        * expression specified in the provided key.
+                        */
+
+                       int     cacheKeyLen = CFStringGetLength(cacheStr) + 1;
+                       char    *cacheKey   = CFAllocatorAllocate(NULL, cacheKeyLen, 0);
+
+                       if (!CFStringGetCString(cacheStr,
+                                               cacheKey,
+                                               cacheKeyLen,
+                                               kCFStringEncodingMacRoman)) {
+                               SCDLog(LOG_DEBUG, CFSTR("CFStringGetCString: could not convert cache key to C string"));
+                               CFAllocatorDeallocate(NULL, cacheKey);
+                               continue;
+                       }
+
+                       reError = regexec(&preg,
+                                         cacheKey,
+                                         0,
+                                         NULL,
+                                         0);
+                       switch (reError) {
+                               case 0 :
+                                       /* we've got a match */
+                                       if (CFDictionaryContainsKey(cacheValue, kSCDData))
+                                               CFArrayAppendValue(keyArray, cacheStr);
+                                       break;
+                               case REG_NOMATCH :
+                                       /* no match */
+                                       break;
+                               default :
+                                       reErrStrLen = regerror(reError,
+                                                              &preg,
+                                                              reErrBuf,
+                                                              sizeof(reErrBuf));
+                                       SCDLog(LOG_DEBUG, CFSTR("regexec(): %s"), reErrBuf);
+                                       break;
+                       }
+                       CFAllocatorDeallocate(NULL, cacheKey);
+               } else {
+                       /*
+                        * only return those keys which are prefixed by the
+                        * provided key string and have data.
+                        */
+                       if (((CFStringGetLength(key) == 0) || CFStringHasPrefix(cacheStr, key)) &&
+                           CFDictionaryContainsKey(cacheValue, kSCDData)) {
+                               CFArrayAppendValue(keyArray, cacheStr);
+                       }
+               }
+       }
+       CFAllocatorDeallocate(NULL, cacheKeys);
+       CFAllocatorDeallocate(NULL, cacheValues);
+
+       if (regexOptions & kSCDRegexKey) {
+               regfree(&preg);
+       }
+
+       *subKeys = keyArray;
+
+       if (regexOptions & kSCDRegexKey) {
+               CFAllocatorDeallocate(NULL, regexStr);
+       }
+
+       return SCD_OK;
+}
+
+
+kern_return_t
+_configlist(mach_port_t                        server,
+           xmlData_t                   keyRef,         /* raw XML bytes */
+           mach_msg_type_number_t      keyLen,
+           int                         regexOptions,
+           xmlDataOut_t                *listRef,       /* raw XML bytes */
+           mach_msg_type_number_t      *listLen,
+           int                         *scd_status
+)
+{
+       kern_return_t           status;
+       serverSessionRef        mySession = getSession(server);
+       CFDataRef               xmlKey;         /* key  (XML serialized) */
+       CFStringRef             key;            /* key  (un-serialized) */
+       CFArrayRef              subKeys;        /* array of CFStringRef's */
+       CFDataRef               xmlList;        /* list (XML serialized) */
+       CFStringRef             xmlError;
+
+       SCDLog(LOG_DEBUG, CFSTR("List keys in configuration database."));
+       SCDLog(LOG_DEBUG, CFSTR("  server = %d"), server);
+
+       /* un-serialize the key */
+       xmlKey = CFDataCreate(NULL, keyRef, keyLen);
+       status = vm_deallocate(mach_task_self(), (vm_address_t)keyRef, keyLen);
+       if (status != KERN_SUCCESS) {
+               SCDLog(LOG_DEBUG, CFSTR("vm_deallocate(): %s"), mach_error_string(status));
+               /* non-fatal???, proceed */
+       }
+       key = CFPropertyListCreateFromXMLData(NULL,
+                                             xmlKey,
+                                             kCFPropertyListImmutable,
+                                             &xmlError);
+       CFRelease(xmlKey);
+       if (xmlError) {
+               SCDLog(LOG_DEBUG, CFSTR("CFPropertyListCreateFromXMLData() key: %s"), xmlError);
+               *scd_status = SCD_FAILED;
+               return KERN_SUCCESS;
+       }
+
+       *scd_status = _SCDList(mySession->session, key, regexOptions, &subKeys);
+       CFRelease(key);
+       if (*scd_status != SCD_OK) {
+               *listRef = NULL;
+               *listLen = 0;
+               return KERN_SUCCESS;
+       }
+
+       /*
+        * serialize the array, copy it into an allocated buffer which will be
+        * released when it is returned as part of a Mach message.
+        */
+       xmlList = CFPropertyListCreateXMLData(NULL, subKeys);
+       CFRelease(subKeys);
+       *listLen = CFDataGetLength(xmlList);
+       status = vm_allocate(mach_task_self(), (void *)listRef, *listLen, TRUE);
+       if (status != KERN_SUCCESS) {
+               SCDLog(LOG_DEBUG, CFSTR("vm_allocate(): %s"), mach_error_string(status));
+               *scd_status = SCD_FAILED;
+               CFRelease(xmlList);
+               *listRef = NULL;
+               *listLen = 0;
+               return KERN_SUCCESS;
+       }
+       bcopy((char *)CFDataGetBytePtr(xmlList), *listRef, *listLen);
+       CFRelease(xmlList);
+
+       return KERN_SUCCESS;
+}
diff --git a/configd.tproj/_configlock.c b/configd.tproj/_configlock.c
new file mode 100644 (file)
index 0000000..ec6b9b1
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include "configd.h"
+#include "configd_server.h"
+#include "session.h"
+
+
+SCDStatus
+_SCDLock(SCDSessionRef session)
+{
+       SCDSessionPrivateRef    sessionPrivate = (SCDSessionPrivateRef)session;
+       serverSessionRef        mySession;
+
+       SCDLog(LOG_DEBUG, CFSTR("_SCDLock:"));
+
+       if ((session == NULL) || (sessionPrivate->server == MACH_PORT_NULL)) {
+               return SCD_NOSESSION;   /* you must have an open session to play */
+       }
+
+       if (SCDOptionGet(NULL, kSCDOptionIsLocked)) {
+               return SCD_LOCKED;      /* sorry, someone (you) already have the lock */
+       }
+
+       /* check credentials */
+       mySession = getSession(sessionPrivate->server);
+       if (mySession->callerEUID != 0) {
+#ifdef DEBUG
+               if (!SCDOptionGet(NULL, kSCDOptionDebug)) {
+#endif /* DEBUG */
+                       return SCD_EACCESS;
+#ifdef DEBUG
+               } else {
+                       SCDLog(LOG_DEBUG, CFSTR("  non-root access granted while debugging"));
+               }
+#endif /* DEBUG */
+       }
+
+       SCDOptionSet(NULL,    kSCDOptionIsLocked, TRUE);        /* global lock flag */
+       SCDOptionSet(session, kSCDOptionIsLocked, TRUE);        /* per-session lock flag */
+
+       /*
+        * defer all (actually, most) changes until the call to _SCDUnlock()
+        */
+       if (cacheData_s) {
+               CFRelease(cacheData_s);
+               CFRelease(changedKeys_s);
+               CFRelease(deferredRemovals_s);
+               CFRelease(removedSessionKeys_s);
+       }
+       cacheData_s          = CFDictionaryCreateMutableCopy(NULL, 0, cacheData);
+       changedKeys_s        = CFSetCreateMutableCopy(NULL, 0, changedKeys);
+       deferredRemovals_s   = CFSetCreateMutableCopy(NULL, 0, deferredRemovals);
+       removedSessionKeys_s = CFSetCreateMutableCopy(NULL, 0, removedSessionKeys);
+
+       /* Add a "locked" mode run loop source for this port */
+       CFRunLoopAddSource(CFRunLoopGetCurrent(), mySession->serverRunLoopSource, CFSTR("locked"));
+
+       return SCD_OK;
+}
+
+
+kern_return_t
+_configlock(mach_port_t server, int *scd_status)
+{
+       serverSessionRef        mySession = getSession(server);
+
+       SCDLog(LOG_DEBUG, CFSTR("Lock configuration database."));
+       SCDLog(LOG_DEBUG, CFSTR("  server = %d"), server);
+
+       *scd_status = _SCDLock(mySession->session);
+       if (*scd_status != SCD_OK) {
+               SCDLog(LOG_DEBUG, CFSTR("  SCDLock(): %s"), SCDError(*scd_status));
+               return KERN_SUCCESS;
+       }
+
+       return KERN_SUCCESS;
+}
diff --git a/configd.tproj/_configopen.c b/configd.tproj/_configopen.c
new file mode 100644 (file)
index 0000000..62b1238
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include "configd.h"
+#include "configd_server.h"
+#include "session.h"
+
+SCDStatus
+_SCDOpen(SCDSessionRef *session, CFStringRef name)
+{
+       SCDSessionPrivateRef    sessionPrivate;
+
+       SCDLog(LOG_DEBUG, CFSTR("_SCDOpen:"));
+       SCDLog(LOG_DEBUG, CFSTR("  name = %@"), name);
+
+       /*
+        * allocate and initialize a new session
+        */
+       sessionPrivate = (SCDSessionPrivateRef)_SCDSessionCreatePrivate();
+       *session       = (SCDSessionRef)sessionPrivate;
+
+       /*
+        * If necessary, initialize the cache and session data dictionaries
+        */
+       if (cacheData == NULL) {
+               cacheData          = CFDictionaryCreateMutable(NULL,
+                                                              0,
+                                                              &kCFTypeDictionaryKeyCallBacks,
+                                                              &kCFTypeDictionaryValueCallBacks);
+               sessionData        = CFDictionaryCreateMutable(NULL,
+                                                              0,
+                                                              &kCFTypeDictionaryKeyCallBacks,
+                                                              &kCFTypeDictionaryValueCallBacks);
+               changedKeys        = CFSetCreateMutable(NULL,
+                                                       0,
+                                                       &kCFTypeSetCallBacks);
+               deferredRemovals   = CFSetCreateMutable(NULL,
+                                                       0,
+                                                       &kCFTypeSetCallBacks);
+               removedSessionKeys = CFSetCreateMutable(NULL,
+                                                       0,
+                                                       &kCFTypeSetCallBacks);
+       }
+
+       return SCD_OK;
+}
+
+
+kern_return_t
+_configopen(mach_port_t                        server,
+           xmlData_t                   nameRef,                /* raw XML bytes */
+           mach_msg_type_number_t      nameLen,
+           mach_port_t                 *newServer,
+           int                         *scd_status)
+{
+       kern_return_t           status;
+       serverSessionRef        mySession, newSession;
+       CFDataRef               xmlName;        /* name (XML serialized) */
+       CFStringRef             name;           /* name (un-serialized) */
+       CFStringRef             xmlError;
+       mach_port_t             oldNotify;
+       CFStringRef             sessionKey;
+       CFDictionaryRef         info;
+       CFMutableDictionaryRef  newInfo;
+       CFMachPortRef           mp;
+
+       SCDLog(LOG_DEBUG, CFSTR("Open new session."));
+       SCDLog(LOG_DEBUG, CFSTR("  server = %d"), server);
+
+       /* un-serialize the name */
+       xmlName = CFDataCreate(NULL, nameRef, nameLen);
+       status = vm_deallocate(mach_task_self(), (vm_address_t)nameRef, nameLen);
+       if (status != KERN_SUCCESS) {
+               CFRelease(xmlName);
+               SCDLog(LOG_DEBUG, CFSTR("vm_deallocate(): %s"), mach_error_string(status));
+               /* non-fatal???, proceed */
+       }
+       name = CFPropertyListCreateFromXMLData(NULL,
+                                              xmlName,
+                                              kCFPropertyListImmutable,
+                                              &xmlError);
+       CFRelease(xmlName);
+       if (xmlError) {
+               SCDLog(LOG_DEBUG, CFSTR("CFPropertyListCreateFromXMLData() name: %s"), xmlError);
+               *scd_status = SCD_FAILED;
+               return KERN_SUCCESS;
+       }
+
+       mySession = getSession(server);
+       if (mySession->session) {
+               CFRelease(name);
+               SCDLog(LOG_DEBUG, CFSTR("  Sorry, this session is already open."));
+               *scd_status = SCD_FAILED;       /* you can't re-open an "open" session */
+               return KERN_SUCCESS;
+       }
+
+       /* Create the server port for this session */
+       mp = CFMachPortCreate(NULL, configdCallback, NULL, NULL);
+
+       /* return the newly allocated port to be used for this session */
+       *newServer = CFMachPortGetPort(mp);
+
+       /*
+        * establish the new session
+        */
+       newSession = addSession(mp);
+
+       /* Create and add a run loop source for the port */
+       newSession->serverRunLoopSource = CFMachPortCreateRunLoopSource(NULL, mp, 0);
+       CFRunLoopAddSource(CFRunLoopGetCurrent(),
+                          newSession->serverRunLoopSource,
+                          kCFRunLoopDefaultMode);
+
+       /*
+        * save the credentials associated with the caller.
+        */
+       newSession->callerEUID = mySession->callerEUID;
+       newSession->callerEGID = mySession->callerEGID;
+
+       *scd_status = _SCDOpen(&newSession->session, name);
+
+       /*
+        * Make the server port accessible to the framework routines.
+        */
+       ((SCDSessionPrivateRef)newSession->session)->server = *newServer;
+
+       /* Request a notification when/if the client dies */
+       status = mach_port_request_notification(mach_task_self(),
+                                               *newServer,
+                                               MACH_NOTIFY_NO_SENDERS,
+                                               1,
+                                               *newServer,
+                                               MACH_MSG_TYPE_MAKE_SEND_ONCE,
+                                               &oldNotify);
+       if (status != KERN_SUCCESS) {
+               SCDLog(LOG_DEBUG, CFSTR("mach_port_request_notification(): %s"), mach_error_string(status));
+               CFRelease(name);
+               cleanupSession(*newServer);
+               *newServer = MACH_PORT_NULL;
+               *scd_status = SCD_FAILED;
+               return KERN_SUCCESS;
+       }
+
+#ifdef DEBUG
+       if (oldNotify != MACH_PORT_NULL) {
+               SCDLog(LOG_DEBUG, CFSTR("_configopen(): why is oldNotify != MACH_PORT_NULL?"));
+       }
+#endif /* DEBUG */
+
+       /*
+        * Save the name of the calling application / plug-in with the session data.
+        */
+       sessionKey = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), *newServer);
+       info = CFDictionaryGetValue(sessionData, sessionKey);
+       if (info) {
+               newInfo = CFDictionaryCreateMutableCopy(NULL, 0, info);
+       } else {
+               newInfo = CFDictionaryCreateMutable(NULL,
+                                                   0,
+                                                   &kCFTypeDictionaryKeyCallBacks,
+                                                   &kCFTypeDictionaryValueCallBacks);
+       }
+       CFDictionarySetValue(newInfo, kSCDName, name);
+       CFRelease(name);
+       CFDictionarySetValue(sessionData, sessionKey, newInfo);
+       CFRelease(newInfo);
+       CFRelease(sessionKey);
+
+       return KERN_SUCCESS;
+}
diff --git a/configd.tproj/_configremove.c b/configd.tproj/_configremove.c
new file mode 100644 (file)
index 0000000..77c5fa9
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include "configd.h"
+#include "session.h"
+
+SCDStatus
+_SCDRemove(SCDSessionRef session, CFStringRef key)
+{
+       SCDSessionPrivateRef    sessionPrivate = (SCDSessionPrivateRef)session;
+       SCDStatus               scd_status = SCD_OK;
+       boolean_t               wasLocked;
+       CFDictionaryRef         dict;
+       CFMutableDictionaryRef  newDict;
+       CFStringRef             sessionKey;
+
+       SCDLog(LOG_DEBUG, CFSTR("_SCDRemove:"));
+       SCDLog(LOG_DEBUG, CFSTR("  key      = %@"), key);
+
+       if ((session == NULL) || (sessionPrivate->server == MACH_PORT_NULL)) {
+               return SCD_NOSESSION;           /* you can't do anything with a closed session */
+       }
+
+       /*
+        * 1. Determine if the cache lock is currently held  and acquire
+        *    the lock if necessary.
+        */
+       wasLocked = SCDOptionGet(NULL, kSCDOptionIsLocked);
+       if (!wasLocked) {
+               scd_status = _SCDLock(session);
+               if (scd_status != SCD_OK) {
+                       SCDLog(LOG_DEBUG, CFSTR("  _SCDLock(): %s"), SCDError(scd_status));
+                       return scd_status;
+               }
+       }
+
+       /*
+        * 2. Ensure that this key exists.
+        */
+       dict = CFDictionaryGetValue(cacheData, key);
+       if ((dict == NULL) || (CFDictionaryContainsKey(dict, kSCDData) == FALSE)) {
+               /* key doesn't exist (or data never defined) */
+               scd_status = SCD_NOKEY;
+               goto done;
+       }
+       newDict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
+
+       /*
+        * 3. Mark this key as "changed". Any "watchers" will be
+        *    notified as soon as the lock is released.
+        */
+       CFSetAddValue(changedKeys, key);
+
+       /*
+        * 4. Add this key to a deferred cleanup list so that, after
+        *    the change notifications are posted, any associated
+        *    regex keys can be removed.
+        */
+       CFSetAddValue(deferredRemovals, key);
+
+       /*
+        * 5. Check if this is a session key and, if so, add it
+        *    to the (session) removal list
+        */
+       sessionKey = CFDictionaryGetValue(newDict, kSCDSession);
+       if (sessionKey) {
+               CFStringRef     removedKey;
+
+               /* We are no longer a session key! */
+               CFDictionaryRemoveValue(newDict, kSCDSession);
+
+               /* add this session key to the (session) removal list */
+               removedKey = CFStringCreateWithFormat(NULL, 0, CFSTR("%@:%@"), sessionKey, key);
+               CFSetAddValue(removedSessionKeys, removedKey);
+               CFRelease(removedKey);
+       }
+
+       /*
+        * 6. Remove data, remove instance, and update/remove
+        *    the dictionary cache entry.
+        */
+       CFDictionaryRemoveValue(newDict, kSCDData);
+       CFDictionaryRemoveValue(newDict, kSCDInstance);
+       if (CFDictionaryGetCount(newDict) > 0) {
+               /* this key is still being "watched" */
+               CFDictionarySetValue(cacheData, key, newDict);
+       } else {
+               /* no information left, remove the empty dictionary */
+               CFDictionaryRemoveValue(cacheData, key);
+       }
+       CFRelease(newDict);
+
+       /*
+        * 7. Release the lock if we acquired it as part of this request.
+        */
+    done:
+       if (!wasLocked)
+               _SCDUnlock(session);
+
+       return scd_status;
+}
+
+
+kern_return_t
+_configremove(mach_port_t              server,
+             xmlData_t                 keyRef,         /* raw XML bytes */
+             mach_msg_type_number_t    keyLen,
+             int                       *scd_status
+)
+{
+       kern_return_t           status;
+       serverSessionRef        mySession = getSession(server);
+       CFDataRef               xmlKey;         /* key  (XML serialized) */
+       CFStringRef             key;            /* key  (un-serialized) */
+       CFStringRef             xmlError;
+
+       SCDLog(LOG_DEBUG, CFSTR("Remove key from configuration database."));
+       SCDLog(LOG_DEBUG, CFSTR("  server = %d"), server);
+
+       /* un-serialize the key */
+       xmlKey = CFDataCreate(NULL, keyRef, keyLen);
+       status = vm_deallocate(mach_task_self(), (vm_address_t)keyRef, keyLen);
+       if (status != KERN_SUCCESS) {
+               SCDLog(LOG_DEBUG, CFSTR("vm_deallocate(): %s"), mach_error_string(status));
+               /* non-fatal???, proceed */
+       }
+       key = CFPropertyListCreateFromXMLData(NULL,
+                                             xmlKey,
+                                             kCFPropertyListImmutable,
+                                             &xmlError);
+       CFRelease(xmlKey);
+       if (xmlError) {
+               SCDLog(LOG_DEBUG, CFSTR("CFPropertyListCreateFromXMLData() key: %s"), xmlError);
+               *scd_status = SCD_FAILED;
+               return KERN_SUCCESS;
+       }
+
+       *scd_status = _SCDRemove(mySession->session, key);
+       CFRelease(key);
+
+       return KERN_SUCCESS;
+}
+
diff --git a/configd.tproj/_configset.c b/configd.tproj/_configset.c
new file mode 100644 (file)
index 0000000..efa671c
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include "configd.h"
+#include "session.h"
+
+SCDStatus
+_SCDSet(SCDSessionRef session, CFStringRef key, SCDHandleRef handle)
+{
+       SCDSessionPrivateRef    sessionPrivate = (SCDSessionPrivateRef)session;
+       SCDStatus               scd_status = SCD_OK;
+       boolean_t               wasLocked;
+       CFDictionaryRef         dict;
+       CFMutableDictionaryRef  newDict;
+       CFNumberRef             num;
+       int                     dictInstance;
+       CFStringRef             sessionKey;
+       CFStringRef             cacheSessionKey;
+
+       SCDLog(LOG_DEBUG, CFSTR("_SCDSet:"));
+       SCDLog(LOG_DEBUG, CFSTR("  key          = %@"), key);
+       SCDLog(LOG_DEBUG, CFSTR("  data         = %@"), SCDHandleGetData(handle));
+       SCDLog(LOG_DEBUG, CFSTR("  instance     = %d"), SCDHandleGetInstance(handle));
+
+       if ((session == NULL) || (sessionPrivate->server == MACH_PORT_NULL)) {
+               return SCD_NOSESSION;           /* you can't do anything with a closed session */
+       }
+
+       /*
+        * 1. Determine if the cache lock is currently held
+        *    and acquire the lock if necessary.
+        */
+       wasLocked = SCDOptionGet(NULL, kSCDOptionIsLocked);
+       if (!wasLocked) {
+               scd_status = _SCDLock(session);
+               if (scd_status != SCD_OK) {
+                       SCDLog(LOG_DEBUG, CFSTR("  _SCDLock(): %s"), SCDError(scd_status));
+                       return scd_status;
+               }
+       }
+
+       /*
+        * 2. Grab the current (or establish a new) dictionary for this key.
+        */
+
+       dict = CFDictionaryGetValue(cacheData, key);
+       if (dict) {
+               newDict = CFDictionaryCreateMutableCopy(NULL,
+                                                       0,
+                                                       dict);
+       } else {
+               newDict = CFDictionaryCreateMutable(NULL,
+                                                   0,
+                                                   &kCFTypeDictionaryKeyCallBacks,
+                                                   &kCFTypeDictionaryValueCallBacks);
+       }
+
+       /*
+        * 3. Make sure that we're not updating the cache with potentially
+        *    stale information.
+        */
+
+       if ((num = CFDictionaryGetValue(newDict, kSCDInstance)) == NULL) {
+               /* if first instance */
+               dictInstance = 0;
+               _SCDHandleSetInstance(handle, dictInstance);
+       } else {
+               (void) CFNumberGetValue(num, kCFNumberIntType, &dictInstance);
+       }
+       if (SCDHandleGetInstance(handle) != dictInstance) {
+               /* data may be based on old information */
+               CFRelease(newDict);
+               scd_status = SCD_STALE;
+               goto done;
+       }
+
+       /*
+        * 4. Update the dictionary entry (data & instance) to be saved to
+        *    the cache.
+        */
+
+       CFDictionarySetValue(newDict, kSCDData, SCDHandleGetData(handle));
+
+       dictInstance++;
+       num = CFNumberCreate(NULL, kCFNumberIntType, &dictInstance);
+       CFDictionarySetValue(newDict, kSCDInstance, num);
+       CFRelease(num);
+       _SCDHandleSetInstance(handle, dictInstance);
+       SCDLog(LOG_DEBUG, CFSTR("  new instance = %d"), SCDHandleGetInstance(handle));
+
+       /*
+        * 5. Since we are updating this key we need to check and, if
+        *    necessary, remove the indication that this key is on
+        *    another session's remove-on-close list.
+        */
+       sessionKey = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), sessionPrivate->server);
+       if (CFDictionaryGetValueIfPresent(newDict, kSCDSession, (void *)&cacheSessionKey) &&
+           !CFEqual(sessionKey, cacheSessionKey)) {
+               CFStringRef     removedKey;
+
+               /* We are no longer a session key! */
+               CFDictionaryRemoveValue(newDict, kSCDSession);
+
+               /* add this session key to the (session) removal list */
+               removedKey = CFStringCreateWithFormat(NULL, 0, CFSTR("%@:%@"), cacheSessionKey, key);
+               CFSetAddValue(removedSessionKeys, removedKey);
+               CFRelease(removedKey);
+       }
+       CFRelease(sessionKey);
+
+       /*
+        * 6. Update the dictionary entry in the cache.
+        */
+
+       CFDictionarySetValue(cacheData, key, newDict);
+       CFRelease(newDict);
+
+       /*
+        * 7. For "new" entries to the cache, check the deferred cleanup
+        *    list. If the key is flagged for removal, remove it from the
+        *    list since any defined regex's for this key are still defined
+        *    and valid. If the key is not flagged then iterate over the
+        *    sessionData dictionary looking for regex keys which match the
+        *    updated key. If a match is found then we mark those keys as
+        *    being watched.
+        */
+
+       if (dictInstance == 1) {
+               if (CFSetContainsValue(deferredRemovals, key)) {
+                       CFSetRemoveValue(deferredRemovals, key);
+               } else {
+                       CFDictionaryApplyFunction(sessionData,
+                                                 (CFDictionaryApplierFunction)_addRegexWatchersBySession,
+                                                 (void *)key);
+               }
+       }
+
+       /*
+        * 8. Mark this key as "changed". Any "watchers" will be notified
+        *    as soon as the lock is released.
+        */
+       CFSetAddValue(changedKeys, key);
+
+       /*
+        * 9. Release the lock if we acquired it as part of this request.
+        */
+    done:
+       if (!wasLocked)
+               _SCDUnlock(session);
+
+       return scd_status;
+}
+
+
+kern_return_t
+_configset(mach_port_t                 server,
+          xmlData_t                    keyRef,         /* raw XML bytes */
+          mach_msg_type_number_t       keyLen,
+          xmlData_t                    dataRef,        /* raw XML bytes */
+          mach_msg_type_number_t       dataLen,
+          int                          oldInstance,
+          int                          *newInstance,
+          int                          *scd_status
+)
+{
+       kern_return_t           status;
+       serverSessionRef        mySession = getSession(server);
+       CFDataRef               xmlKey;         /* key  (XML serialized) */
+       CFStringRef             key;            /* key  (un-serialized) */
+       CFDataRef               xmlData;        /* data (XML serialized) */
+       CFPropertyListRef       data;           /* data (un-serialized) */
+       SCDHandleRef            handle;
+       CFStringRef             xmlError;
+
+       SCDLog(LOG_DEBUG, CFSTR("Set key to configuration database."));
+       SCDLog(LOG_DEBUG, CFSTR("  server = %d"), server);
+
+       /* un-serialize the key */
+       xmlKey = CFDataCreate(NULL, keyRef, keyLen);
+       status = vm_deallocate(mach_task_self(), (vm_address_t)keyRef, keyLen);
+       if (status != KERN_SUCCESS) {
+               SCDLog(LOG_DEBUG, CFSTR("vm_deallocate(): %s"), mach_error_string(status));
+               /* non-fatal???, proceed */
+       }
+       key = CFPropertyListCreateFromXMLData(NULL,
+                                             xmlKey,
+                                             kCFPropertyListImmutable,
+                                             &xmlError);
+       CFRelease(xmlKey);
+       if (xmlError) {
+               SCDLog(LOG_DEBUG, CFSTR("CFPropertyListCreateFromXMLData() key: %s"), xmlError);
+               *scd_status = SCD_FAILED;
+               return KERN_SUCCESS;
+       }
+
+       /* un-serialize the data */
+       xmlData = CFDataCreate(NULL, dataRef, dataLen);
+       status = vm_deallocate(mach_task_self(), (vm_address_t)dataRef, dataLen);
+       if (status != KERN_SUCCESS) {
+               SCDLog(LOG_DEBUG, CFSTR("vm_deallocate(): %s"), mach_error_string(status));
+               /* non-fatal???, proceed */
+       }
+       data = CFPropertyListCreateFromXMLData(NULL,
+                                              xmlData,
+                                              kCFPropertyListImmutable,
+                                              &xmlError);
+       CFRelease(xmlData);
+       if (xmlError) {
+               SCDLog(LOG_DEBUG, CFSTR("CFPropertyListCreateFromXMLData() data: %s"), xmlError);
+               CFRelease(key);
+               *scd_status = SCD_FAILED;
+               return KERN_SUCCESS;
+       }
+
+       handle = SCDHandleInit();
+       SCDHandleSetData(handle, data);
+       _SCDHandleSetInstance(handle, oldInstance);
+       *scd_status = _SCDSet(mySession->session, key, handle);
+       if (*scd_status == SCD_OK) {
+               *newInstance = SCDHandleGetInstance(handle);
+       }
+       SCDHandleRelease(handle);
+       CFRelease(key);
+       CFRelease(data);
+
+       return KERN_SUCCESS;
+}
diff --git a/configd.tproj/_configtouch.c b/configd.tproj/_configtouch.c
new file mode 100644 (file)
index 0000000..318e6db
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include "configd.h"
+#include "session.h"
+
+SCDStatus
+_SCDTouch(SCDSessionRef session, CFStringRef key)
+{
+       SCDSessionPrivateRef    sessionPrivate = (SCDSessionPrivateRef)session;
+       SCDStatus               scd_status;
+       boolean_t               wasLocked;
+       SCDHandleRef            handle;
+       CFPropertyListRef       value;
+
+       SCDLog(LOG_DEBUG, CFSTR("_SCDTouch:"));
+       SCDLog(LOG_DEBUG, CFSTR("  key = %@"), key);
+
+       if ((session == NULL) || (sessionPrivate->server == MACH_PORT_NULL)) {
+               return SCD_NOSESSION;           /* you can't do anything with a closed session */
+       }
+
+       /*
+        * 1. Determine if the cache lock is currently held by this session
+        *    and acquire the lock if necessary.
+        */
+       wasLocked = SCDOptionGet(NULL, kSCDOptionIsLocked);
+       if (!wasLocked) {
+               scd_status = _SCDLock(session);
+               if (scd_status != SCD_OK) {
+                       SCDLog(LOG_DEBUG, CFSTR("  _SCDLock(): %s"), SCDError(scd_status));
+                       return scd_status;
+               }
+       }
+
+       /*
+        * 2. Grab the current (or establish a new) cache entry for this key.
+        */
+       scd_status = _SCDGet(session, key, &handle);
+       switch (scd_status) {
+               case SCD_NOKEY :
+                       /* cache entry does not exist, create */
+                       handle = SCDHandleInit();
+                       value  = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent());
+                       SCDLog(LOG_DEBUG, CFSTR("  new time stamp = %@"), value);
+                       SCDHandleSetData(handle, value);
+                       CFRelease(value);
+                       break;
+
+               case SCD_OK :
+                       /* cache entry exists, update */
+                       value = SCDHandleGetData(handle);
+                       if (CFGetTypeID(value) == CFDateGetTypeID()) {
+                               /* if value is a CFDate */
+                               value = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent());
+                               SCDLog(LOG_DEBUG, CFSTR("  new time stamp = %@"), value);
+                               SCDHandleSetData(handle, value);
+                               CFRelease(value);
+                       } /* else, we'll just save the data (again) to bump the instance */
+                       break;
+
+               default :
+                       SCDLog(LOG_DEBUG, CFSTR("  _SCDGet(): %s"), SCDError(scd_status));
+                       goto done;
+       }
+
+       scd_status = _SCDSet(session, key, handle);
+       SCDHandleRelease(handle);
+
+    done :
+
+       /*
+        * 8. Release the lock if we acquired it as part of this request.
+        */
+       if (!wasLocked)
+               _SCDUnlock(session);
+
+       return SCD_OK;
+}
+
+
+kern_return_t
+_configtouch(mach_port_t                       server,
+            xmlData_t                  keyRef,         /* raw XML bytes */
+            mach_msg_type_number_t     keyLen,
+            int                                *scd_status
+)
+{
+       kern_return_t           status;
+       serverSessionRef        mySession = getSession(server);
+       CFDataRef               xmlKey;         /* key  (XML serialized) */
+       CFStringRef             key;            /* key  (un-serialized) */
+       CFStringRef             xmlError;
+
+       SCDLog(LOG_DEBUG, CFSTR("Touch key in configuration database."));
+       SCDLog(LOG_DEBUG, CFSTR("  server = %d"), server);
+
+       /* un-serialize the key */
+       xmlKey = CFDataCreate(NULL, keyRef, keyLen);
+       status = vm_deallocate(mach_task_self(), (vm_address_t)keyRef, keyLen);
+       if (status != KERN_SUCCESS) {
+               SCDLog(LOG_DEBUG, CFSTR("vm_deallocate(): %s"), mach_error_string(status));
+               /* non-fatal???, proceed */
+       }
+       key = CFPropertyListCreateFromXMLData(NULL,
+                                             xmlKey,
+                                             kCFPropertyListImmutable,
+                                             &xmlError);
+       CFRelease(xmlKey);
+       if (xmlError) {
+               SCDLog(LOG_DEBUG, CFSTR("CFPropertyListCreateFromXMLData() key: %s"), xmlError);
+               *scd_status = SCD_FAILED;
+               return KERN_SUCCESS;
+       }
+
+       *scd_status = _SCDTouch(mySession->session, key);
+       CFRelease(key);
+
+       return KERN_SUCCESS;
+}
diff --git a/configd.tproj/_configunlock.c b/configd.tproj/_configunlock.c
new file mode 100644 (file)
index 0000000..421eee6
--- /dev/null
@@ -0,0 +1,296 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+
+#include "configd.h"
+#include "configd_server.h"
+#include "session.h"
+
+
+static void
+_notifyWatchers()
+{
+       CFIndex                 keyCnt;
+       void                    **keys;
+
+       if ((keyCnt = CFSetGetCount(changedKeys)) == 0)
+               return;         /* if nothing to do */
+
+       keys = CFAllocatorAllocate(NULL, keyCnt * sizeof(CFStringRef), 0);
+       CFSetGetValues(changedKeys, keys);
+       while (--keyCnt >= 0) {
+               CFDictionaryRef         dict;
+               CFArrayRef              sessionsWatchingKey;
+               CFIndex                 watcherCnt;
+               void                    **watchers;
+               CFDictionaryRef         info;
+               CFMutableDictionaryRef  newInfo;
+               CFArrayRef              changes;
+               CFMutableArrayRef       newChanges;
+
+               dict = CFDictionaryGetValue(cacheData, (CFStringRef)keys[keyCnt]);
+               if ((dict == NULL) || (CFDictionaryContainsKey(dict, kSCDWatchers) == FALSE)) {
+                       /* key doesn't exist or nobody cares if it changed */
+                       continue;
+               }
+
+               /*
+                * Add this key to the list of changes for each of the
+                * sessions which is "watching".
+                */
+               sessionsWatchingKey = CFDictionaryGetValue(dict, kSCDWatchers);
+               watcherCnt = CFArrayGetCount(sessionsWatchingKey);
+               watchers   = CFAllocatorAllocate(NULL, watcherCnt * sizeof(CFNumberRef), 0);
+               CFArrayGetValues(sessionsWatchingKey,
+                                CFRangeMake(0, CFArrayGetCount(sessionsWatchingKey)),
+                                watchers);
+               while (--watcherCnt >= 0) {
+                       CFStringRef     sessionKey;
+
+                       sessionKey = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@"), watchers[watcherCnt]);
+                       info = CFDictionaryGetValue(sessionData, sessionKey);
+                       if (info) {
+                               newInfo = CFDictionaryCreateMutableCopy(NULL, 0, info);
+                       } else {
+                               newInfo = CFDictionaryCreateMutable(NULL,
+                                                                   0,
+                                                                   &kCFTypeDictionaryKeyCallBacks,
+                                                                   &kCFTypeDictionaryValueCallBacks);
+                       }
+
+                       changes = CFDictionaryGetValue(newInfo, kSCDChangedKeys);
+                       if (changes) {
+                               newChanges = CFArrayCreateMutableCopy(NULL, 0, changes);
+                       } else {
+                               newChanges = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+                       }
+
+                       if (CFArrayContainsValue(newChanges,
+                                                CFRangeMake(0, CFArrayGetCount(newChanges)),
+                                                (CFStringRef)keys[keyCnt]) == FALSE) {
+                               CFArrayAppendValue(newChanges, (CFStringRef)keys[keyCnt]);
+                       }
+                       CFDictionarySetValue(newInfo, kSCDChangedKeys, newChanges);
+                       CFRelease(newChanges);
+                       CFDictionarySetValue(sessionData, sessionKey, newInfo);
+                       CFRelease(newInfo);
+                       CFRelease(sessionKey);
+
+                       /*
+                        * flag this session as needing a kick
+                        */
+                       if (needsNotification == NULL)
+                               needsNotification = CFSetCreateMutable(NULL,
+                                                                      0,
+                                                                      &kCFTypeSetCallBacks);
+                       CFSetAddValue(needsNotification, watchers[watcherCnt]);
+               }
+               CFAllocatorDeallocate(NULL, watchers);
+       }
+       CFAllocatorDeallocate(NULL, keys);
+
+       /*
+        * The list of changed keys have been updated for any sessions
+        * monitoring changes to the "cache". The next step, handled by
+        * the "configd" server, is to push out any needed notifications.
+        */
+       CFSetRemoveAllValues(changedKeys);
+
+}
+
+
+static void
+_processDeferredRemovals()
+{
+       CFIndex                 keyCnt;
+       void                    **keys;
+
+       if ((keyCnt = CFSetGetCount(deferredRemovals)) == 0)
+               return;         /* if nothing to do */
+
+       keys = CFAllocatorAllocate(NULL, keyCnt * sizeof(CFStringRef), 0);
+       CFSetGetValues(deferredRemovals, keys);
+       while (--keyCnt >= 0) {
+               CFDictionaryApplyFunction(sessionData,
+                                         (CFDictionaryApplierFunction)_removeRegexWatchersBySession,
+                                         keys[keyCnt]);
+       }
+       CFAllocatorDeallocate(NULL, keys);
+
+       /*
+        * All regex keys associated with removed cache dictionary keys have
+        * been removed. Start the list fresh again.
+        */
+       CFSetRemoveAllValues(deferredRemovals);
+
+       return;
+}
+
+
+static void
+_cleanupRemovedSessionKeys(const void *value, void *context)
+{
+       CFStringRef             removedKey = (CFStringRef)value;
+       CFRange                 dRange;
+       CFStringRef             sessionKey;
+       CFStringRef             key;
+       CFDictionaryRef         sessionDict;
+       CFArrayRef              sessionKeys;
+       CFIndex                 i;
+       CFMutableDictionaryRef  newSessionDict;
+
+       dRange     = CFStringFind(removedKey, CFSTR(":"), 0);
+       sessionKey = CFStringCreateWithSubstring(NULL,
+                                                removedKey,
+                                                CFRangeMake(0, dRange.location));
+       key        = CFStringCreateWithSubstring(NULL,
+                                                removedKey,
+                                                CFRangeMake(dRange.location+dRange.length,
+                                                            CFStringGetLength(removedKey)-dRange.location-dRange.length));
+
+       /*
+        * remove the key from the session key list
+        */
+       sessionDict = CFDictionaryGetValue(sessionData, sessionKey);
+       if (!sessionDict) {
+               /* if no session */
+               goto done;
+       }
+
+       sessionKeys = CFDictionaryGetValue(sessionDict, kSCDSessionKeys);
+       if (!sessionKeys) {
+               /* if no session keys */
+               goto done;
+       }
+
+       i = CFArrayGetFirstIndexOfValue(sessionKeys,
+                                       CFRangeMake(0, CFArrayGetCount(sessionKeys)),
+                                       key);
+       if (i == -1) {
+               /* if this session key has already been removed */
+               goto done;
+       }
+
+       newSessionDict = CFDictionaryCreateMutableCopy(NULL, 0, sessionDict);
+       if (CFArrayGetCount(sessionKeys) == 1) {
+               /* remove the last (session) key */
+               CFDictionaryRemoveValue(newSessionDict, kSCDSessionKeys);
+       } else {
+               CFMutableArrayRef       newSessionKeys;
+
+               /* remove the (session) key */
+               newSessionKeys = CFArrayCreateMutableCopy(NULL, 0, sessionKeys);
+               CFArrayRemoveValueAtIndex(newSessionKeys, i);
+               CFDictionarySetValue(newSessionDict, kSCDSessionKeys, newSessionKeys);
+               CFRelease(newSessionKeys);
+       }
+       CFDictionarySetValue(sessionData, sessionKey, newSessionDict);
+       CFRelease(newSessionDict);
+
+    done:
+
+       CFRelease(sessionKey);
+       CFRelease(key);
+
+       return;
+}
+
+
+SCDStatus
+_SCDUnlock(SCDSessionRef session)
+{
+       SCDSessionPrivateRef    sessionPrivate = (SCDSessionPrivateRef)session;
+       serverSessionRef        mySession;
+
+       SCDLog(LOG_DEBUG, CFSTR("_SCDUnlock:"));
+
+       if ((session == NULL) || (sessionPrivate->server == MACH_PORT_NULL)) {
+               return SCD_NOSESSION;
+       }
+
+       if (!SCDOptionGet(NULL, kSCDOptionIsLocked) || !SCDOptionGet(session, kSCDOptionIsLocked)) {
+               return SCD_NEEDLOCK;    /* sorry, you don't have the lock */
+       }
+
+       /*
+        * all of the changes can be committed to the (real) cache.
+        */
+       CFDictionaryRemoveAllValues(cacheData_s);
+       CFSetRemoveAllValues       (changedKeys_s);
+       CFSetRemoveAllValues       (deferredRemovals_s);
+       CFSetRemoveAllValues       (removedSessionKeys_s);
+
+#ifdef DEBUG
+       SCDLog(LOG_DEBUG, CFSTR("keys I changed           = %@"), changedKeys);
+       SCDLog(LOG_DEBUG, CFSTR("keys flagged for removal = %@"), deferredRemovals);
+       SCDLog(LOG_DEBUG, CFSTR("keys I'm watching        = %@"), sessionPrivate->keys);
+       SCDLog(LOG_DEBUG, CFSTR("regex keys I'm watching  = %@"), sessionPrivate->reKeys);
+#endif /* DEBUG */
+
+       /*
+        * push notifications to any session watching those keys which
+        * were recently changed.
+        */
+       _notifyWatchers();
+
+       /*
+        * process any deferred key deletions.
+        */
+       _processDeferredRemovals();
+
+       /*
+        * clean up any removed session keys
+        */
+       CFSetApplyFunction(removedSessionKeys, _cleanupRemovedSessionKeys, NULL);
+       CFSetRemoveAllValues(removedSessionKeys);
+
+#ifdef DEBUG
+       SCDLog(LOG_DEBUG, CFSTR("sessions to notify = %@"), needsNotification);
+#endif /* DEBUG */
+
+       /* Remove the "locked" run loop source for this port */
+       mySession = getSession(sessionPrivate->server);
+       CFRunLoopRemoveSource(CFRunLoopGetCurrent(), mySession->serverRunLoopSource, CFSTR("locked"));
+
+       SCDOptionSet(NULL,    kSCDOptionIsLocked, FALSE);       /* global lock flag */
+       SCDOptionSet(session, kSCDOptionIsLocked, FALSE);       /* per-session lock flag */
+
+       return SCD_OK;
+}
+
+
+kern_return_t
+_configunlock(mach_port_t server, int *scd_status)
+{
+       serverSessionRef        mySession = getSession(server);
+
+       SCDLog(LOG_DEBUG, CFSTR("Unlock configuration database."));
+       SCDLog(LOG_DEBUG, CFSTR("  server = %d"), server);
+
+       *scd_status = _SCDUnlock(mySession->session);
+       if (*scd_status != SCD_OK) {
+               SCDLog(LOG_DEBUG, CFSTR("  _SCDUnlock(): %s"), SCDError(*scd_status));
+               return KERN_SUCCESS;
+       }
+
+       return KERN_SUCCESS;
+}
diff --git a/configd.tproj/_notifyadd.c b/configd.tproj/_notifyadd.c
new file mode 100644 (file)
index 0000000..5ca354a
--- /dev/null
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include "configd.h"
+#include "session.h"
+
+SCDStatus
+_SCDNotifierAdd(SCDSessionRef session, CFStringRef key, int regexOptions)
+{
+       SCDSessionPrivateRef    sessionPrivate = (SCDSessionPrivateRef)session;
+
+       SCDLog(LOG_DEBUG, CFSTR("_SCDNotifierAdd:"));
+       SCDLog(LOG_DEBUG, CFSTR("  key          = %@"), key);
+       SCDLog(LOG_DEBUG, CFSTR("  regexOptions = %0o"), regexOptions);
+
+       if ((session == NULL) || (sessionPrivate->server == MACH_PORT_NULL)) {
+               return SCD_NOSESSION;           /* you can't do anything with a closed session */
+       }
+
+       /*
+        * add new key after checking if key has already been defined
+        */
+       if (regexOptions & kSCDRegexKey) {
+               if (CFSetContainsValue(sessionPrivate->reKeys, key))
+                       return SCD_EXISTS;              /* sorry, key already exists in notifier list */
+               CFSetAddValue(sessionPrivate->reKeys, key);     /* add key to this sessions notifier list */
+       } else {
+               if (CFSetContainsValue(sessionPrivate->keys, key))
+                       return SCD_EXISTS;              /* sorry, key already exists in notifier list */
+               CFSetAddValue(sessionPrivate->keys, key);       /* add key to this sessions notifier list */
+       }
+
+       if (regexOptions & kSCDRegexKey) {
+               CFStringRef             sessionKey;
+               int                     regexStrLen;
+               char                    *regexStr;
+               CFMutableDataRef        regexData;
+               int                     reError;
+               char                    reErrBuf[256];
+               int                     reErrStrLen;
+               addContext              context;
+               CFDictionaryRef         info;
+               CFMutableDictionaryRef  newInfo;
+               CFArrayRef              rKeys;
+               CFMutableArrayRef       newRKeys;
+               CFArrayRef              rData;
+               CFMutableArrayRef       newRData;
+
+               /*
+                * We are adding a regex key. As such, we need to flag
+                * any keys currently in the cache.
+                */
+
+               /* 1. Extract a C String version of the key pattern string. */
+
+               regexStrLen = CFStringGetLength(key) + 1;
+               regexStr    = CFAllocatorAllocate(NULL, regexStrLen, 0);
+               if (!CFStringGetCString(key,
+                                       regexStr,
+                                       regexStrLen,
+                                       kCFStringEncodingMacRoman)) {
+                       SCDLog(LOG_DEBUG, CFSTR("CFStringGetCString: could not convert regex key to C string"));
+                       CFAllocatorDeallocate(NULL, regexStr);
+                       return SCD_FAILED;
+               }
+
+               /* 2. Compile the regular expression from the pattern string. */
+
+               regexData = CFDataCreateMutable(NULL, sizeof(regex_t));
+               CFDataSetLength(regexData, sizeof(regex_t));
+               reError = regcomp((regex_t *)CFDataGetBytePtr(regexData),
+                                 regexStr,
+                                 REG_EXTENDED);
+               CFAllocatorDeallocate(NULL, regexStr);
+               if (reError != 0) {
+                       reErrStrLen = regerror(reError,
+                                              (regex_t *)CFDataGetBytePtr(regexData),
+                                              reErrBuf,
+                                              sizeof(reErrBuf));
+                       SCDLog(LOG_DEBUG, CFSTR("regcomp() key: %s"), reErrBuf);
+                       CFRelease(regexData);
+                       return SCD_FAILED;
+               }
+
+               /*
+                * 3. Iterate over the current keys and add this session as a "watcher"
+                *    for any key already defined in the cache.
+                */
+
+               context.session = sessionPrivate;
+               context.preg    = (regex_t *)CFDataGetBytePtr(regexData);
+               CFDictionaryApplyFunction(cacheData,
+                                         (CFDictionaryApplierFunction)_addRegexWatcherByKey,
+                                         &context);
+
+               /*
+                * 4. We also need to save this key and the associated regex data
+                *    for any subsequent additions.
+                */
+               sessionKey = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), sessionPrivate->server);
+
+               info = CFDictionaryGetValue(sessionData, sessionKey);
+               if (info) {
+                       newInfo = CFDictionaryCreateMutableCopy(NULL, 0, info);
+               } else {
+                       newInfo = CFDictionaryCreateMutable(NULL,
+                                                           0,
+                                                           &kCFTypeDictionaryKeyCallBacks,
+                                                           &kCFTypeDictionaryValueCallBacks);
+               }
+
+               rKeys = CFDictionaryGetValue(newInfo, kSCDRegexKeys);
+               if ((rKeys == NULL) ||
+                   (CFArrayContainsValue(rKeys,
+                                         CFRangeMake(0, CFArrayGetCount(rKeys)),
+                                         key) == FALSE)) {
+                       rData = CFDictionaryGetValue(newInfo, kSCDRegexData);
+                       if (rKeys) {
+                               newRKeys = CFArrayCreateMutableCopy(NULL, 0, rKeys);
+                               newRData = CFArrayCreateMutableCopy(NULL, 0, rData);
+                       } else {
+                               newRKeys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+                               newRData = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+                       }
+
+                       /* save the regex key */
+                       CFArrayAppendValue(newRKeys, key);
+                       CFDictionarySetValue(newInfo, kSCDRegexKeys, newRKeys);
+                       CFRelease(newRKeys);
+                       /* ...and the compiled expression */
+                       CFArrayAppendValue(newRData, regexData);
+                       CFDictionarySetValue(newInfo, kSCDRegexData, newRData);
+                       CFRelease(newRData);
+                       CFDictionarySetValue(sessionData, sessionKey, newInfo);
+               }
+               CFRelease(newInfo);
+               CFRelease(sessionKey);
+       } else {
+               CFNumberRef     sessionNum;
+
+               /*
+                * We are watching a specific key. As such, update the
+                * cache to mark our interest in any changes.
+                */
+               sessionNum = CFNumberCreate(NULL, kCFNumberIntType, &sessionPrivate->server);
+               _addWatcher(sessionNum, key);
+               CFRelease(sessionNum);
+       }
+
+       return SCD_OK;
+}
+
+
+kern_return_t
+_notifyadd(mach_port_t                         server,
+          xmlData_t                    keyRef,         /* raw XML bytes */
+          mach_msg_type_number_t       keyLen,
+          int                          regexOptions,
+          int                          *scd_status
+)
+{
+       kern_return_t           status;
+       serverSessionRef        mySession = getSession(server);
+       CFDataRef               xmlKey;         /* key  (XML serialized) */
+       CFStringRef             key;            /* key  (un-serialized) */
+       CFStringRef             xmlError;
+
+       SCDLog(LOG_DEBUG, CFSTR("Add notification key for this session."));
+       SCDLog(LOG_DEBUG, CFSTR("  server = %d"), server);
+
+       /* un-serialize the key */
+       xmlKey = CFDataCreate(NULL, keyRef, keyLen);
+       status = vm_deallocate(mach_task_self(), (vm_address_t)keyRef, keyLen);
+       if (status != KERN_SUCCESS) {
+               SCDLog(LOG_DEBUG, CFSTR("vm_deallocate(): %s"), mach_error_string(status));
+               /* non-fatal???, proceed */
+       }
+       key = CFPropertyListCreateFromXMLData(NULL,
+                                             xmlKey,
+                                             kCFPropertyListImmutable,
+                                             &xmlError);
+       CFRelease(xmlKey);
+       if (xmlError) {
+               SCDLog(LOG_DEBUG, CFSTR("CFPropertyListCreateFromXMLData() key: %s"), xmlError);
+               *scd_status = SCD_FAILED;
+               return KERN_SUCCESS;
+       }
+
+       *scd_status = _SCDNotifierAdd(mySession->session, key, regexOptions);
+       CFRelease(key);
+
+       return KERN_SUCCESS;
+}
diff --git a/configd.tproj/_notifycancel.c b/configd.tproj/_notifycancel.c
new file mode 100644 (file)
index 0000000..5a77701
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include <unistd.h>
+
+#include "configd.h"
+#include "session.h"
+
+
+SCDStatus
+_SCDNotifierCancel(SCDSessionRef session)
+{
+       SCDSessionPrivateRef    sessionPrivate = (SCDSessionPrivateRef)session;
+
+       SCDLog(LOG_DEBUG, CFSTR("_SCDNotifierCancel:"));
+
+       if (session == NULL) {
+               return SCD_NOSESSION;   /* you must have an open session to play */
+       }
+
+       /*
+        * cleanup any mach port based notifications.
+        */
+       if (sessionPrivate->notifyPort != MACH_PORT_NULL) {
+               (void) mach_port_destroy(mach_task_self(), sessionPrivate->notifyPort);
+               sessionPrivate->notifyPort = MACH_PORT_NULL;
+       }
+
+       /*
+        * cleanup any file based notifications.
+        */
+       if (sessionPrivate->notifyFile >= 0) {
+               SCDLog(LOG_DEBUG, CFSTR("  closing (notification) fd %d"), sessionPrivate->notifyFile);
+               (void) close(sessionPrivate->notifyFile);
+               sessionPrivate->notifyFile = -1;
+       }
+
+       /*
+        * cleanup any signal notifications.
+        */
+       if (sessionPrivate->notifySignal > 0) {
+               (void) mach_port_destroy(mach_task_self(), sessionPrivate->notifySignalTask);
+               sessionPrivate->notifySignal     = 0;
+               sessionPrivate->notifySignalTask = TASK_NULL;
+       }
+
+       /* remove this session from the to-be-notified list */
+       if (needsNotification) {
+               CFNumberRef     num;
+
+               num = CFNumberCreate(NULL, kCFNumberIntType, &sessionPrivate->server);
+               CFSetRemoveValue(needsNotification, num);
+               CFRelease(num);
+
+               if (CFSetGetCount(needsNotification) == 0) {
+                       CFRelease(needsNotification);
+                       needsNotification = NULL;
+               }
+       }
+
+       /* set notifier inactive */
+       sessionPrivate->notifyStatus = NotifierNotRegistered;
+
+       return SCD_OK;
+}
+
+
+kern_return_t
+_notifycancel(mach_port_t      server,
+             int               *scd_status)
+{
+       serverSessionRef        mySession = getSession(server);
+
+       SCDLog(LOG_DEBUG, CFSTR("Cancel requested notifications."));
+       SCDLog(LOG_DEBUG, CFSTR("  server = %d"), server);
+
+       *scd_status = _SCDNotifierCancel(mySession->session);
+       if (*scd_status != SCD_OK) {
+               SCDLog(LOG_DEBUG, CFSTR("  SCDNotifierCancel(): %s"), SCDError(*scd_status));
+       }
+
+       return KERN_SUCCESS;
+}
diff --git a/configd.tproj/_notifychanges.c b/configd.tproj/_notifychanges.c
new file mode 100644 (file)
index 0000000..19ad363
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include "configd.h"
+#include "session.h"
+
+SCDStatus
+_SCDNotifierGetChanges(SCDSessionRef session, CFArrayRef *notifierKeys)
+{
+       SCDSessionPrivateRef    sessionPrivate = (SCDSessionPrivateRef)session;
+       CFStringRef             sessionKey;
+       CFDictionaryRef         info;
+       CFMutableDictionaryRef  newInfo;
+
+       SCDLog(LOG_DEBUG, CFSTR("_SCDNotifierGetChanges:"));
+
+       if ((session == NULL) || (sessionPrivate->server == MACH_PORT_NULL)) {
+               return SCD_NOSESSION;
+       }
+
+       sessionKey = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), sessionPrivate->server);
+       info = CFDictionaryGetValue(sessionData, sessionKey);
+       if ((info == NULL) ||
+           (CFDictionaryContainsKey(info, kSCDChangedKeys) == FALSE)) {
+               CFRelease(sessionKey);
+               *notifierKeys = CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks);;
+               return SCD_OK;
+       }
+       newInfo = CFDictionaryCreateMutableCopy(NULL, 0, info);
+
+       *notifierKeys = CFDictionaryGetValue(newInfo, kSCDChangedKeys);
+       CFRetain(*notifierKeys);
+
+       CFDictionaryRemoveValue(newInfo, kSCDChangedKeys);
+       if (CFDictionaryGetCount(newInfo) > 0) {
+               CFDictionarySetValue(sessionData, sessionKey, newInfo);
+       } else {
+               CFDictionaryRemoveValue(sessionData, sessionKey);
+       }
+       CFRelease(newInfo);
+       CFRelease(sessionKey);
+
+       return SCD_OK;
+}
+
+
+kern_return_t
+_notifychanges(mach_port_t                     server,
+              xmlDataOut_t                     *listRef,       /* raw XML bytes */
+              mach_msg_type_number_t           *listLen,
+              int                              *scd_status
+)
+{
+       kern_return_t           status;
+       serverSessionRef        mySession = getSession(server);
+       CFArrayRef              notifierKeys;   /* array of CFStringRef's */
+       CFDataRef               xmlList;        /* list (XML serialized) */
+
+       SCDLog(LOG_DEBUG, CFSTR("List notification keys which have changed."));
+       SCDLog(LOG_DEBUG, CFSTR("  server = %d"), server);
+
+       *scd_status = _SCDNotifierGetChanges(mySession->session, &notifierKeys);
+       if (*scd_status != SCD_OK) {
+               *listRef = NULL;
+               *listLen = 0;
+               return KERN_SUCCESS;
+       }
+
+       /*
+        * serialize the array, copy it into an allocated buffer which will be
+        * released when it is returned as part of a Mach message.
+        */
+       xmlList = CFPropertyListCreateXMLData(NULL, notifierKeys);
+       CFRelease(notifierKeys);
+       *listLen = CFDataGetLength(xmlList);
+       status = vm_allocate(mach_task_self(), (void *)listRef, *listLen, TRUE);
+       if (status != KERN_SUCCESS) {
+               SCDLog(LOG_DEBUG, CFSTR("vm_allocate(): %s"), mach_error_string(status));
+               CFRelease(xmlList);
+               *listRef = NULL;
+               *listLen = 0;
+               *scd_status = SCD_FAILED;
+               return KERN_SUCCESS;
+       }
+
+       bcopy((char *)CFDataGetBytePtr(xmlList), *listRef, *listLen);
+       CFRelease(xmlList);
+
+       return KERN_SUCCESS;
+}
diff --git a/configd.tproj/_notifyremove.c b/configd.tproj/_notifyremove.c
new file mode 100644 (file)
index 0000000..e16f2a2
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include "configd.h"
+#include "session.h"
+
+SCDStatus
+_SCDNotifierRemove(SCDSessionRef session, CFStringRef key, int regexOptions)
+{
+       SCDSessionPrivateRef    sessionPrivate = (SCDSessionPrivateRef)session;
+
+       SCDLog(LOG_DEBUG, CFSTR("_SCDNotifierRemove:"));
+       SCDLog(LOG_DEBUG, CFSTR("  key          = %@"), key);
+       SCDLog(LOG_DEBUG, CFSTR("  regexOptions = %0o"), regexOptions);
+
+       if ((session == NULL) || (sessionPrivate->server == MACH_PORT_NULL)) {
+               return SCD_NOSESSION;           /* you can't do anything with a closed session */
+       }
+
+       /*
+        * remove key from this sessions notifier list after checking that
+        * it was previously defined.
+        */
+       if (regexOptions & kSCDRegexKey) {
+               if (!CFSetContainsValue(sessionPrivate->reKeys, key))
+                       return SCD_NOKEY;               /* sorry, key does not exist in notifier list */
+               CFSetRemoveValue(sessionPrivate->reKeys, key);  /* remove key from this sessions notifier list */
+       } else {
+               if (!CFSetContainsValue(sessionPrivate->keys, key))
+                       return SCD_NOKEY;               /* sorry, key does not exist in notifier list */
+               CFSetRemoveValue(sessionPrivate->keys, key);    /* remove key from this sessions notifier list */
+       }
+
+       if (regexOptions & kSCDRegexKey) {
+               CFStringRef             sessionKey;
+               CFDictionaryRef         info;
+               CFMutableDictionaryRef  newInfo;
+               CFArrayRef              rKeys;
+               CFMutableArrayRef       newRKeys;
+               CFArrayRef              rData;
+               CFMutableArrayRef       newRData;
+               CFIndex                 i;
+               CFDataRef               regexData;
+               removeContext           context;
+
+               sessionKey = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), sessionPrivate->server);
+
+               info    = CFDictionaryGetValue(sessionData, sessionKey);
+               newInfo = CFDictionaryCreateMutableCopy(NULL, 0, info);
+
+               rKeys    = CFDictionaryGetValue(newInfo, kSCDRegexKeys);
+               newRKeys = CFArrayCreateMutableCopy(NULL, 0, rKeys);
+
+               rData    = CFDictionaryGetValue(newInfo, kSCDRegexData);
+               newRData = CFArrayCreateMutableCopy(NULL, 0, rData);
+
+               i = CFArrayGetFirstIndexOfValue(newRKeys,
+                                               CFRangeMake(0, CFArrayGetCount(newRData)),
+                                               key);
+               regexData = CFArrayGetValueAtIndex(newRData, i);
+
+               context.session = sessionPrivate;
+               context.preg    = (regex_t *)CFDataGetBytePtr(regexData);
+               CFDictionaryApplyFunction(cacheData,
+                                         (CFDictionaryApplierFunction)_removeRegexWatcherByKey,
+                                         &context);
+
+               /* remove the regex key */
+               CFArrayRemoveValueAtIndex(newRKeys, i);
+               if (CFArrayGetCount(newRKeys) > 0) {
+                       CFDictionarySetValue(newInfo, kSCDRegexKeys, newRKeys);
+               } else {
+                       CFDictionaryRemoveValue(newInfo, kSCDRegexKeys);
+               }
+               CFRelease(newRKeys);
+
+               /* ...and the compiled expression */
+               regfree((regex_t *)CFDataGetBytePtr(regexData));
+               CFArrayRemoveValueAtIndex(newRData, i);
+               if (CFArrayGetCount(newRData) > 0) {
+                       CFDictionarySetValue(newInfo, kSCDRegexData, newRData);
+               } else {
+                       CFDictionaryRemoveValue(newInfo, kSCDRegexData);
+               }
+               CFRelease(newRData);
+
+               /* save the updated session data */
+               CFDictionarySetValue(sessionData, sessionKey, newInfo);
+               CFRelease(newInfo);
+
+               CFRelease(sessionKey);
+       } else {
+               CFNumberRef     sessionNum;
+
+               /*
+                * We are watching a specific key. As such, update the
+                * cache to mark our interest in any changes.
+                */
+
+               sessionNum = CFNumberCreate(NULL, kCFNumberIntType, &sessionPrivate->server);
+               _removeWatcher(sessionNum, key);
+               CFRelease(sessionNum);
+       }
+
+       return SCD_OK;
+}
+
+
+kern_return_t
+_notifyremove(mach_port_t              server,
+             xmlData_t                 keyRef,         /* raw XML bytes */
+             mach_msg_type_number_t    keyLen,
+             int                       regexOptions,
+             int                       *scd_status
+)
+{
+       kern_return_t           status;
+       serverSessionRef        mySession = getSession(server);
+       CFDataRef               xmlKey;         /* key  (XML serialized) */
+       CFStringRef             key;            /* key  (un-serialized) */
+       CFStringRef             xmlError;
+
+       SCDLog(LOG_DEBUG, CFSTR("Remove notification key for this session."));
+       SCDLog(LOG_DEBUG, CFSTR("  server = %d"), server);
+
+       /* un-serialize the key */
+       xmlKey = CFDataCreate(NULL, keyRef, keyLen);
+       status = vm_deallocate(mach_task_self(), (vm_address_t)keyRef, keyLen);
+       if (status != KERN_SUCCESS) {
+               SCDLog(LOG_DEBUG, CFSTR("vm_deallocate(): %s"), mach_error_string(status));
+               /* non-fatal???, proceed */
+       }
+       key = CFPropertyListCreateFromXMLData(NULL,
+                                             xmlKey,
+                                             kCFPropertyListImmutable,
+                                             &xmlError);
+       CFRelease(xmlKey);
+       if (xmlError) {
+               SCDLog(LOG_DEBUG, CFSTR("CFPropertyListCreateFromXMLData() key: %s"), xmlError);
+               *scd_status = SCD_FAILED;
+               return KERN_SUCCESS;
+       }
+
+       *scd_status = _SCDNotifierRemove(mySession->session, key, regexOptions);
+       CFRelease(key);
+
+       return KERN_SUCCESS;
+}
+
diff --git a/configd.tproj/_notifyviafd.c b/configd.tproj/_notifyviafd.c
new file mode 100644 (file)
index 0000000..7eedbfc
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "configd.h"
+#include "session.h"
+
+
+SCDStatus
+_SCDNotifierInformViaFD(SCDSessionRef  session,
+                      int32_t          identifier,
+                      int              *fd)
+{
+       SCDSessionPrivateRef    sessionPrivate = (SCDSessionPrivateRef)session;
+       int                     sock;
+       CFStringRef             sessionKey;
+       CFDictionaryRef         info;
+
+       SCDLog(LOG_DEBUG, CFSTR("_SCDNotifierInformViaFD:"));
+
+       if ((session == NULL) || (sessionPrivate->server == MACH_PORT_NULL)) {
+               return SCD_NOSESSION;   /* you must have an open session to play */
+       }
+
+       if (sessionPrivate->notifyStatus != NotifierNotRegistered) {
+               /* sorry, you can only have one notification registered at once */
+               return SCD_NOTIFIERACTIVE;
+       }
+
+       if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
+               SCDLog(LOG_NOTICE, CFSTR("socket: %s"), strerror(errno));
+               return SCD_FAILED;
+       }
+
+       *fd = sock;
+
+       /* push out a notification if any changes are pending */
+       sessionKey = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), sessionPrivate->server);
+       info = CFDictionaryGetValue(sessionData, sessionKey);
+       CFRelease(sessionKey);
+       if (info && CFDictionaryContainsKey(info, kSCDChangedKeys)) {
+               CFNumberRef     sessionNum;
+
+               if (needsNotification == NULL)
+                       needsNotification = CFSetCreateMutable(NULL,
+                                                              0,
+                                                              &kCFTypeSetCallBacks);
+
+               sessionNum = CFNumberCreate(NULL, kCFNumberIntType, &sessionPrivate->server);
+               CFSetAddValue(needsNotification, sessionNum);
+               CFRelease(sessionNum);
+       }
+
+       return SCD_OK;
+}
+
+
+kern_return_t
+_notifyviafd(mach_port_t               server,
+            xmlData_t                  pathRef,
+            mach_msg_type_number_t     pathLen,
+            int                        identifier,
+            int                        *scd_status
+)
+{
+       kern_return_t           status;
+       serverSessionRef        mySession = getSession(server);
+       SCDSessionPrivateRef    sessionPrivate = (SCDSessionPrivateRef)mySession->session;
+       struct sockaddr_un      un;
+       int                     sock;
+       int                     bufSiz  = sizeof(sessionPrivate->notifyFileIdentifier);
+       int                     nbioYes = 1;
+
+       SCDLog(LOG_DEBUG, CFSTR("Send message via UNIX domain socket when a notification key changes."));
+       SCDLog(LOG_DEBUG, CFSTR("  server = %d"), server);
+       SCDLog(LOG_DEBUG, CFSTR("  path   = %s"), pathRef);
+
+       /*
+        * if socket currently open, close it!
+        */
+       /* validate the UNIX domain socket path */
+       if (pathLen > (sizeof(un.sun_path) - 1)) {
+               SCDLog(LOG_NOTICE, CFSTR("domain socket path length too long!"));
+               status = vm_deallocate(mach_task_self(), (vm_address_t)pathRef, pathLen);
+               if (status != KERN_SUCCESS) {
+                       SCDLog(LOG_DEBUG, CFSTR("vm_deallocate(): %s"), mach_error_string(status));
+                       /* non-fatal???, proceed */
+               }
+               *scd_status = SCD_FAILED;
+               return KERN_SUCCESS;
+       }
+
+       /* un-serialize the UNIX domain socket path */
+       un.sun_family = AF_UNIX;
+       bcopy(pathRef, un.sun_path, pathLen);
+       un.sun_path[pathLen] = '\0';
+       status = vm_deallocate(mach_task_self(), (vm_address_t)pathRef, pathLen);
+       if (status != KERN_SUCCESS) {
+               SCDLog(LOG_DEBUG, CFSTR("vm_deallocate(): %s"), mach_error_string(status));
+               /* non-fatal???, proceed */
+       }
+
+       /* do common sanity checks, get socket */
+       *scd_status = _SCDNotifierInformViaFD(mySession->session, identifier, &sock);
+
+       /* check status of _SCDNotifierInformViaFD() */
+       if (*scd_status != SCD_OK) {
+               return KERN_SUCCESS;
+       }
+
+       /* establish the connection, get ready for a read() */
+       if (connect(sock, (struct sockaddr *)&un, sizeof(un)) == -1) {
+               SCDLog(LOG_DEBUG, CFSTR("connect: %s"), strerror(errno));
+               (void) close(sock);
+               sessionPrivate->notifyStatus = NotifierNotRegistered;
+               sessionPrivate->notifyFile   = -1;
+               *scd_status = SCD_FAILED;
+               return KERN_SUCCESS;
+       }
+
+       SCDLog(LOG_NOTICE, CFSTR("  fd     = %d"), sock);
+       (void) unlink(un.sun_path);
+
+       if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &bufSiz, sizeof(bufSiz)) < 0) {
+               SCDLog(LOG_DEBUG, CFSTR("setsockopt: %s"), strerror(errno));
+               (void) close(sock);
+               *scd_status = SCD_FAILED;
+               return KERN_SUCCESS;
+       }
+
+       if (ioctl(sock, FIONBIO, &nbioYes) == -1) {
+               SCDLog(LOG_DEBUG, CFSTR("ioctl(,FIONBIO,): %s"), strerror(errno));
+               (void) close(sock);
+               *scd_status = SCD_FAILED;
+               return KERN_SUCCESS;
+       }
+
+       /* set notifier active */
+       sessionPrivate->notifyStatus         = Using_NotifierInformViaFD;
+       sessionPrivate->notifyFile           = sock;
+       sessionPrivate->notifyFileIdentifier = identifier;
+
+       return KERN_SUCCESS;
+}
diff --git a/configd.tproj/_notifyviaport.c b/configd.tproj/_notifyviaport.c
new file mode 100644 (file)
index 0000000..ac270ec
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include "configd.h"
+#include "session.h"
+
+SCDStatus
+_SCDNotifierInformViaMachPort(SCDSessionRef session, mach_msg_id_t identifier, mach_port_t *port)
+{
+       SCDSessionPrivateRef    sessionPrivate = (SCDSessionPrivateRef)session;
+       CFStringRef             sessionKey;
+       CFDictionaryRef         info;
+
+       SCDLog(LOG_DEBUG, CFSTR("_SCDNotifierInformViaMachPort:"));
+
+       if ((session == NULL) || (sessionPrivate->server == MACH_PORT_NULL)) {
+               return SCD_NOSESSION;   /* you must have an open session to play */
+       }
+
+       if (sessionPrivate->notifyStatus != NotifierNotRegistered) {
+               /* sorry, you can only have one notification registered at once */
+               return SCD_NOTIFIERACTIVE;
+       }
+
+       if (*port == MACH_PORT_NULL) {
+               /* sorry, you must specify a valid mach port */
+               return SCD_INVALIDARGUMENT;
+       }
+
+       /* push out a notification if any changes are pending */
+       sessionKey = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), sessionPrivate->server);
+       info = CFDictionaryGetValue(sessionData, sessionKey);
+       CFRelease(sessionKey);
+       if (info && CFDictionaryContainsKey(info, kSCDChangedKeys)) {
+               CFNumberRef     sessionNum;
+
+               if (needsNotification == NULL)
+                       needsNotification = CFSetCreateMutable(NULL,
+                                                              0,
+                                                              &kCFTypeSetCallBacks);
+
+               sessionNum = CFNumberCreate(NULL, kCFNumberIntType, &sessionPrivate->server);
+               CFSetAddValue(needsNotification, sessionNum);
+               CFRelease(sessionNum);
+       }
+
+       return SCD_OK;
+}
+
+
+kern_return_t
+_notifyviaport(mach_port_t     server,
+              mach_port_t      port,
+              mach_msg_id_t    identifier,
+              int              *scd_status
+)
+{
+       serverSessionRef        mySession = getSession(server);
+       SCDSessionPrivateRef    sessionPrivate = (SCDSessionPrivateRef)mySession->session;
+
+       SCDLog(LOG_DEBUG, CFSTR("Send mach message when a notification key changes."));
+       SCDLog(LOG_DEBUG, CFSTR("  server     = %d"), server);
+       SCDLog(LOG_DEBUG, CFSTR("  port       = %d"), port);
+       SCDLog(LOG_DEBUG, CFSTR("  message id = %d"), identifier);
+
+       if (sessionPrivate->notifyPort != MACH_PORT_NULL) {
+               SCDLog(LOG_DEBUG, CFSTR("  destroying old callback mach port %d"), sessionPrivate->notifyPort);
+               (void) mach_port_destroy(mach_task_self(), sessionPrivate->notifyPort);
+       }
+
+       *scd_status = _SCDNotifierInformViaMachPort(mySession->session, identifier, &port);
+
+       if (*scd_status == SCD_OK) {
+               /* save notification port, requested identifier, and set notifier active */
+               sessionPrivate->notifyStatus         = Using_NotifierInformViaMachPort;
+               sessionPrivate->notifyPort           = port;
+               sessionPrivate->notifyPortIdentifier = identifier;
+       }
+
+       return KERN_SUCCESS;
+}
diff --git a/configd.tproj/_notifyviasignal.c b/configd.tproj/_notifyviasignal.c
new file mode 100644 (file)
index 0000000..fc10d1b
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include "configd.h"
+#include "configd_server.h"
+#include "session.h"
+
+SCDStatus
+_SCDNotifierInformViaSignal(SCDSessionRef session, pid_t pid, int sig)
+{
+       SCDSessionPrivateRef    sessionPrivate = (SCDSessionPrivateRef)session;
+       CFStringRef             sessionKey;
+       CFDictionaryRef         info;
+
+       SCDLog(LOG_DEBUG, CFSTR("_SCDNotifierInformViaSignal:"));
+
+       if ((session == NULL) || (sessionPrivate->server == MACH_PORT_NULL)) {
+               return SCD_NOSESSION;   /* you must have an open session to play */
+       }
+
+       if (sessionPrivate->notifyStatus != NotifierNotRegistered) {
+               /* sorry, you can only have one notification registered at once */
+               return SCD_NOTIFIERACTIVE;
+       }
+
+       if ((sig <= 0) || (sig > NSIG)) {
+               /* sorry, you must specify a valid signal */
+               return SCD_INVALIDARGUMENT;
+       }
+
+       /* push out a notification if any changes are pending */
+       sessionKey = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), sessionPrivate->server);
+       info = CFDictionaryGetValue(sessionData, sessionKey);
+       CFRelease(sessionKey);
+       if (info && CFDictionaryContainsKey(info, kSCDChangedKeys)) {
+               CFNumberRef     sessionNum;
+
+               if (needsNotification == NULL)
+                       needsNotification = CFSetCreateMutable(NULL,
+                                                              0,
+                                                              &kCFTypeSetCallBacks);
+
+               sessionNum = CFNumberCreate(NULL, kCFNumberIntType, &sessionPrivate->server);
+               CFSetAddValue(needsNotification, sessionNum);
+               CFRelease(sessionNum);
+       }
+
+       return SCD_OK;
+}
+
+
+kern_return_t
+_notifyviasignal(mach_port_t   server,
+                task_t         task,
+                int            sig,
+                int            *scd_status)
+{
+       serverSessionRef        mySession  = getSession(server);
+       SCDSessionPrivateRef    sessionPrivate = (SCDSessionPrivateRef)mySession->session;
+#if    defined(DEBUG) || defined(NOTYET)
+       kern_return_t           status;
+#endif
+#ifdef NOTYET
+       mach_port_t             oldNotify;
+#endif /* NOTYET */
+
+       SCDLog(LOG_DEBUG, CFSTR("Send signal when a notification key changes."));
+       SCDLog(LOG_DEBUG, CFSTR("  server = %d"), server);
+       SCDLog(LOG_DEBUG, CFSTR("  task   = %d"), task);
+       SCDLog(LOG_DEBUG, CFSTR("  signal = %d"), sig);
+
+       *scd_status = _SCDNotifierInformViaSignal(mySession->session, 0, sig);  /* pid is N/A for server */
+       if (*scd_status != SCD_OK) {
+               if (task != TASK_NULL) {
+                       (void) mach_port_destroy(mach_task_self(), task);
+               }
+               return KERN_SUCCESS;
+       }
+
+#ifdef DEBUG
+       {       mach_port_type_t        pt;
+
+               status = mach_port_type(mach_task_self(), task, &pt);
+               if (status == MACH_MSG_SUCCESS) {
+                       char    rights[8], *rp = &rights[0];
+
+                       if (pt & MACH_PORT_TYPE_SEND)
+                               *rp++ = 'S';
+                       if (pt & MACH_PORT_TYPE_RECEIVE)
+                               *rp++ = 'R';
+                       if (pt & MACH_PORT_TYPE_SEND_ONCE)
+                               *rp++ = 'O';
+                       if (pt & MACH_PORT_TYPE_PORT_SET)
+                               *rp++ = 'P';
+                       if (pt & MACH_PORT_TYPE_DEAD_NAME)
+                               *rp++ = 'D';
+                       *rp = '\0';
+
+                       SCDLog(LOG_DEBUG, CFSTR("Task %d, port rights = %s"), task, rights);
+               }
+       }
+#endif /* DEBUG */
+
+#ifdef NOTYET
+       /* Request a notification when/if the client dies */
+       status = mach_port_request_notification(mach_task_self(),
+                                               task,
+                                               MACH_NOTIFY_DEAD_NAME,
+                                               1,
+                                               task,
+                                               MACH_MSG_TYPE_MAKE_SEND_ONCE,
+                                               &oldNotify);
+       if (status != KERN_SUCCESS) {
+               SCDLog(LOG_DEBUG, CFSTR("mach_port_request_notification(): %s"), mach_error_string(status));
+               *scd_status = SCD_FAILED;
+               return KERN_SUCCESS;
+       }
+
+#ifdef DEBUG
+       if (oldNotify != MACH_PORT_NULL) {
+               SCDLog(LOG_DEBUG, CFSTR("_notifyviasignal(): why is oldNotify != MACH_PORT_NULL?"));
+       }
+#endif /* DEBUG */
+
+       SCDLog(LOG_DEBUG, CFSTR("Adding task notification port %d to the server's port set"), task);
+       status = mach_port_move_member(mach_task_self(), task, server_ports);
+       if (status != KERN_SUCCESS) {
+               SCDLog(LOG_DEBUG, CFSTR("mach_port_move_member(): %s"), mach_error_string(status));
+               *scd_status = SCD_FAILED;
+               return KERN_SUCCESS;
+       }
+#endif /* NOTYET */
+
+       sessionPrivate->notifyStatus     = Using_NotifierInformViaSignal;
+       sessionPrivate->notifySignal     = sig;
+       sessionPrivate->notifySignalTask = task;
+
+       return KERN_SUCCESS;
+}
diff --git a/configd.tproj/_snapshot.c b/configd.tproj/_snapshot.c
new file mode 100644 (file)
index 0000000..b21d96a
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include <fcntl.h>
+#include <paths.h>
+#include <unistd.h>
+
+#include "configd.h"
+#include "configd_server.h"
+#include "session.h"
+
+
+#define        SNAPSHOT_PATH_CACHE     _PATH_VARTMP "configd-cache.xml"
+#define        SNAPSHOT_PATH_SESSION   _PATH_VARTMP "configd-session.xml"
+
+
+SCDStatus
+_SCDSnapshot(SCDSessionRef session)
+{
+       int                     fd;
+       CFDataRef               xmlData;
+
+       SCDLog(LOG_DEBUG, CFSTR("_SCDSnapshot:"));
+
+       /* Save a snapshot of the "cache" data */
+
+       (void) unlink(SNAPSHOT_PATH_CACHE);
+       fd = open(SNAPSHOT_PATH_CACHE, O_WRONLY|O_CREAT|O_TRUNC, 0644);
+       if (fd < 0) {
+               return SCD_FAILED;
+       }
+
+       xmlData = CFPropertyListCreateXMLData(NULL, cacheData);
+       (void) write(fd, CFDataGetBytePtr(xmlData), CFDataGetLength(xmlData));
+       (void) close(fd);
+       CFRelease(xmlData);
+
+       /* Save a snapshot of the "session" data */
+
+       (void) unlink(SNAPSHOT_PATH_SESSION);
+       fd = open(SNAPSHOT_PATH_SESSION, O_WRONLY|O_CREAT|O_TRUNC, 0644);
+       if (fd < 0) {
+               return SCD_FAILED;
+       }
+
+       /* Save a snapshot of the "session" data */
+
+       xmlData = CFPropertyListCreateXMLData(NULL, sessionData);
+       (void) write(fd, CFDataGetBytePtr(xmlData), CFDataGetLength(xmlData));
+       (void) close(fd);
+       CFRelease(xmlData);
+
+       return SCD_OK;
+}
+
+
+kern_return_t
+_snapshot(mach_port_t server, int *scd_status)
+{
+       serverSessionRef        mySession = getSession(server);
+
+       SCDLog(LOG_DEBUG, CFSTR("Snapshot configuration database."));
+
+       *scd_status = _SCDSnapshot(mySession->session);
+       if (*scd_status != SCD_OK) {
+               SCDLog(LOG_DEBUG, CFSTR("  SCDUnlock(): %s"), SCDError(*scd_status));
+       }
+
+       return KERN_SUCCESS;
+}
diff --git a/configd.tproj/config.defs b/configd.tproj/config.defs
new file mode 100644 (file)
index 0000000..cc2513a
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Import the real "config.defs" file from the framework.
+ *
+ *   The ultimate goal is to have a single ".defs" file in
+ *   the project which is used to generate a shared "config.h"
+ *   header, the "configUser.c" file used by (and included in)
+ *   the built framework, and the "configServer.c" file used
+ *   by the server.
+ *
+ *   To date, I have been unable to determine the needed flags
+ *   and/or rules to add to the Makefile to ensure that any
+ *   changes made to the real projects ".defs" file results in
+ *   the[re-]generation of both framework and servers MiG files.
+ *
+ *   As such... IF YOU CHANGE THE ".defs" FILE, MAKE CLEAN!
+ */
+
+#import "../SystemConfiguration.fproj/config.defs"
\ No newline at end of file
diff --git a/configd.tproj/configd.h b/configd.tproj/configd.h
new file mode 100644 (file)
index 0000000..05ffe70
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#ifndef _S_CONFIGD_H
+#define _S_CONFIGD_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/cdefs.h>
+#include <mach/mach.h>
+#include <mach/mach_error.h>
+
+#include <SystemConfiguration/SCD.h>
+#include "config_types.h"
+#include "SCDPrivate.h"
+#include "_SCD.h"
+
+
+__BEGIN_DECLS
+__END_DECLS
+
+#endif /* !_S_CONFIGD_H */
diff --git a/configd.tproj/configd.m b/configd.tproj/configd.m
new file mode 100644 (file)
index 0000000..dad2d5a
--- /dev/null
@@ -0,0 +1,257 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+/* 
+ * Modification History 
+ * 24 March 2000       Allan Nathanson (ajn@apple.com)
+ *                     - created
+ */
+
+#include <stdio.h>
+#include <sysexits.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <paths.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <objc/objc-runtime.h>
+
+#include "configd.h"
+#include "configd_server.h"
+#include "plugin_support.h"
+
+
+const char *signames[] = {
+       ""    , "HUP" , "INT"   , "QUIT", "ILL"  , "TRAP", "ABRT", "EMT" ,
+       "FPE" , "KILL", "BUS"   , "SEGV", "SYS"  , "PIPE", "ALRM", "TERM",
+       "URG" , "STOP", "TSTP"  , "CONT", "CHLD" , "TTIN", "TTOU", "IO"  ,
+       "XCPU", "XFSZ", "VTALRM", "PROF", "WINCH", "INFO", "USR1", "USR2"
+};
+
+
+void
+usage(const char *prog)
+{
+       SCDLog(LOG_INFO, CFSTR("%s: [-d] [-v] [-b] [-t plugin-bundle-path]"), prog);
+       SCDLog(LOG_INFO, CFSTR("options:"));
+       SCDLog(LOG_INFO, CFSTR("\t-d\tenable debugging"));
+       SCDLog(LOG_INFO, CFSTR("\t-v\tenable verbose logging"));
+       SCDLog(LOG_INFO, CFSTR("\t-b\tdisable loading of ALL plug-ins"));
+       SCDLog(LOG_INFO, CFSTR("\t-t\tload/test the specified plug-in"));
+       SCDLog(LOG_INFO, CFSTR("\t\t  (Note: only the plug-in will be started)"), prog);
+       exit (EX_USAGE);
+}
+
+
+void
+catcher(int signum)
+{
+       /*
+        * log the signal
+        *
+        * Note: we can't use SCDLog() since the signal may be received while the
+        *       logging thread lock is held.
+        */
+       if (SCDOptionGet(NULL, kSCDOptionUseSyslog)) {
+               syslog (LOG_INFO, "caught SIG%s"  , signames[signum]);
+       } else {
+               fprintf(stderr,   "caught SIG%s\n", signames[signum]);
+               fflush (stderr);
+       }
+
+       return;
+}
+
+static void
+parent_exit(int i)
+{
+       _exit (0);
+}
+
+static int
+fork_child()
+{
+       int     child_pid;
+       int     fd;
+
+       signal(SIGTERM, parent_exit);
+       child_pid = fork();
+       switch (child_pid) {
+               case -1: {
+                       return -1;
+               }
+               case 0: {
+                       /* child: becomes the daemon (see below) */
+                       signal(SIGTERM, SIG_DFL);
+                       break;
+               }
+               default: {
+                       /* parent: wait for signal, then exit */
+                       int     status;
+
+                       (void) wait4(child_pid, (int *)&status, 0, 0);
+                       if (WIFEXITED(status)) {
+                               fprintf(stderr,
+                                       "*** configd (daemon) failed to start, exit status=%d",
+                                       WEXITSTATUS(status));
+                       } else {
+                               fprintf(stderr,
+                                       "*** configd (daemon) failed to start, received signal=%d",
+                                       WTERMSIG(status));
+                       }
+                       fflush (stderr);
+                       exit (EX_SOFTWARE);
+               }
+       }
+
+       if (setsid() == -1)
+               return -1;
+
+       (void)chdir("/");
+
+       if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
+               (void)dup2(fd, STDIN_FILENO);
+               (void)dup2(fd, STDOUT_FILENO);
+               (void)dup2(fd, STDERR_FILENO);
+               if (fd > 2)
+                       (void)close(fd);
+       }
+
+       return 0;
+}
+
+int
+main (int argc, const char *argv[])
+{
+       extern int              optind;
+       int                     opt;
+       const char              *prog = argv[0];
+       boolean_t               loadBundles = TRUE;
+       const char              *testBundle = NULL;
+       struct sigaction        nact;
+
+       /* process any arguments */
+
+       while ((opt = getopt(argc, argv, "bdt:v")) != -1) {
+               switch(opt) {
+                       case 'b':
+                               loadBundles = FALSE;
+                               break;
+                       case 'd':
+                               SCDOptionSet(NULL, kSCDOptionDebug, TRUE);
+                               break;
+                       case 't':
+                               testBundle = optarg;
+                               break;
+                       case 'v':
+                               SCDOptionSet(NULL, kSCDOptionVerbose, TRUE);
+                               break;
+                       case '?':
+                       default :
+                               usage(prog);
+               }
+       }
+       argc -= optind;
+       argv += optind;
+
+       /*
+        * display an error if configd is already running and we are
+        * not executing/testing a bundle.
+        */
+       if ((testBundle == NULL) && (server_active() == TRUE)) {
+               exit (EX_UNAVAILABLE);
+       }
+
+       /* get ready */
+
+       SCDOptionSet(NULL, kSCDOptionIsServer, TRUE);           /* Use the config API's "server" code */
+       SCDOptionSet(NULL, kSCDOptionUseCFRunLoop, TRUE);       /* Use the CFRunLoop */
+
+       /* check credentials */
+       if (getuid() != 0) {
+#ifdef DEBUG
+               if (!SCDOptionGet(NULL, kSCDOptionDebug)) {
+#endif /* DEBUG */
+                       fprintf(stderr, "%s: permission denied.\n", prog);
+                       exit (EX_NOPERM);
+#ifdef DEBUG
+               }
+#endif /* DEBUG */
+       }
+
+       if ((testBundle == NULL) && !SCDOptionGet(NULL, kSCDOptionDebug)) {
+               if (fork_child() == -1) {
+                       fprintf(stderr, "configd: fork() failed, %s\n", strerror(errno));
+                       exit (1);
+               }
+               /* now the child process, parent waits in fork_child */
+
+               /* log via syslog() facility */
+               openlog("configd", (LOG_NDELAY | LOG_PID), LOG_DAEMON);
+               SCDOptionSet(NULL, kSCDOptionUseSyslog, TRUE);
+       }
+
+       /* add signal handler to catch a SIGPIPE */
+
+       nact.sa_handler = catcher;
+       sigemptyset(&nact.sa_mask);
+       nact.sa_flags = SA_RESTART;
+
+       if (sigaction(SIGPIPE, &nact, NULL) == -1) {
+               SCDLog(LOG_ERR,
+                      CFSTR("sigaction(SIGPIPE, ...) failed: %s"),
+                      strerror(errno));
+       }
+
+       /* get set */
+
+       objc_setMultithreaded(YES);
+
+       if (testBundle == NULL) {
+               /* initialize primary (cache management) thread */
+               server_init();
+
+               /* load/initialize/start bundles into the secondary thread */
+               if (loadBundles) {
+                       plugin_init();
+               } else {
+                       if (!SCDOptionGet(NULL, kSCDOptionDebug)) {
+                           /* synchronize with parent process */
+                           kill(getppid(), SIGTERM);
+                       }
+               }
+       }
+
+       /* go */
+
+       if (testBundle == NULL) {
+               /* start primary (cache management) thread */
+               server_loop();
+       } else {
+               /* load/initialize/start specified plug-in */
+               plugin_exec((void *)testBundle);
+       }
+
+       exit (EX_OK);   // insure the process exit status is 0
+       return 0;       // ...and make main fit the ANSI spec.
+}
diff --git a/configd.tproj/configd_server.c b/configd.tproj/configd_server.c
new file mode 100644 (file)
index 0000000..69a1911
--- /dev/null
@@ -0,0 +1,315 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include <servers/bootstrap.h>
+#include <sysexits.h>
+
+#include "configd.h"
+#include "configd_server.h"
+#include "notify_server.h"
+#include "session.h"
+#include "notify.h"
+
+/* MiG generated externals and functions */
+extern struct rpc_subsystem    _config_subsystem;
+extern boolean_t               config_server(mach_msg_header_t *, mach_msg_header_t *);
+
+/* server state information */
+CFMachPortRef          configd_port;           /* configd server port (for new session requests) */
+
+
+boolean_t
+config_demux(mach_msg_header_t *request, mach_msg_header_t *reply)
+{
+       boolean_t                       processed = FALSE;
+
+       mach_msg_format_0_trailer_t     *trailer;
+
+       /* Feed the request into the ("MiG" generated) server */
+       if (!processed &&
+           (request->msgh_id >= _config_subsystem.start && request->msgh_id < _config_subsystem.end)) {
+               serverSessionRef        thisSession;
+
+               thisSession = getSession(request->msgh_local_port);
+               if (thisSession) {
+                       /*
+                        * Get the caller's credentials (eUID/eGID) from the message trailer.
+                        */
+                       trailer = (mach_msg_security_trailer_t *)((vm_offset_t)request +
+                                                                 round_msg(request->msgh_size));
+
+                       if ((trailer->msgh_trailer_type == MACH_MSG_TRAILER_FORMAT_0) &&
+                           (trailer->msgh_trailer_size >= MACH_MSG_TRAILER_FORMAT_0_SIZE)) {
+                               thisSession->callerEUID = trailer->msgh_sender.val[0];
+                               thisSession->callerEGID = trailer->msgh_sender.val[1];
+                               SCDLog(LOG_DEBUG, CFSTR("caller has eUID = %d, eGID = %d"),
+                                      thisSession->callerEUID,
+                                      thisSession->callerEGID);
+                       } else {
+                               static boolean_t warned = FALSE;
+
+                               if (!warned) {
+                                       SCDLog(LOG_WARNING, CFSTR("caller's credentials not available."));
+                                       warned = TRUE;
+                               }
+                               thisSession->callerEUID = 0;
+                               thisSession->callerEGID = 0;
+                       }
+              }
+
+               /*
+                * Process configd requests.
+                */
+               processed = config_server(request, reply);
+       }
+
+       if (!processed &&
+           (request->msgh_id >= MACH_NOTIFY_FIRST && request->msgh_id < MACH_NOTIFY_LAST)) {
+               processed = notify_server(request, reply);
+       }
+
+       if (!processed) {
+               SCDLog(LOG_WARNING, CFSTR("unknown message received"));
+               exit (EX_OSERR);
+       }
+
+       return processed;
+}
+
+
+void
+configdCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
+{
+       mig_reply_error_t       *bufRequest = msg;
+       mig_reply_error_t       *bufReply   = CFAllocatorAllocate(NULL, _config_subsystem.maxsize, 0);
+       mach_msg_return_t       mr;
+       int                     options;
+
+       /* we have a request message */
+       (void) config_demux(&bufRequest->Head, &bufReply->Head);
+
+       if (!(bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) && (bufReply->RetCode != KERN_SUCCESS)) {
+
+               if (bufReply->RetCode == MIG_NO_REPLY) {
+                       /*
+                        * This return code is a little tricky -- it appears that the
+                        * demux routine found an error of some sort, but since that
+                        * error would not normally get returned either to the local
+                        * user or the remote one, we pretend it's ok.
+                        */
+                       CFAllocatorDeallocate(NULL, bufReply);
+                       return;
+               }
+
+               /*
+                * destroy any out-of-line data in the request buffer but don't destroy
+                * the reply port right (since we need that to send an error message).
+                */
+               bufRequest->Head.msgh_remote_port = MACH_PORT_NULL;
+               mach_msg_destroy(&bufRequest->Head);
+       }
+
+       if (bufReply->Head.msgh_remote_port == MACH_PORT_NULL) {
+               /* no reply port, so destroy the reply */
+               if (bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) {
+                       mach_msg_destroy(&bufReply->Head);
+               }
+               CFAllocatorDeallocate(NULL, bufReply);
+               return;
+       }
+
+       /*
+        * send reply.
+        *
+        * We don't want to block indefinitely because the client
+        * isn't receiving messages from the reply port.
+        * If we have a send-once right for the reply port, then
+        * this isn't a concern because the send won't block.
+        * If we have a send right, we need to use MACH_SEND_TIMEOUT.
+        * To avoid falling off the kernel's fast RPC path unnecessarily,
+        * we only supply MACH_SEND_TIMEOUT when absolutely necessary.
+        */
+
+       options = MACH_SEND_MSG;
+       if (MACH_MSGH_BITS_REMOTE(bufReply->Head.msgh_bits) == MACH_MSG_TYPE_MOVE_SEND_ONCE) {
+               options |= MACH_SEND_TIMEOUT;
+       }
+       mr = mach_msg(&bufReply->Head,          /* msg */
+                     options,                  /* option */
+                     bufReply->Head.msgh_size, /* send_size */
+                     0,                        /* rcv_size */
+                     MACH_PORT_NULL,           /* rcv_name */
+                     MACH_MSG_TIMEOUT_NONE,    /* timeout */
+                     MACH_PORT_NULL);          /* notify */
+
+
+       /* Has a message error occurred? */
+       switch (mr) {
+               case MACH_SEND_INVALID_DEST:
+               case MACH_SEND_TIMED_OUT:
+                       /* the reply can't be delivered, so destroy it */
+                       mach_msg_destroy(&bufReply->Head);
+                       break;
+
+               default :
+                       /* Includes success case.  */
+                       break;
+       }
+
+       CFAllocatorDeallocate(NULL, bufReply);
+}
+
+
+boolean_t
+server_active()
+{
+       kern_return_t           status;
+       mach_port_t             bootstrap_port;
+       boolean_t               active;
+
+       /* Getting bootstrap server port */
+       status = task_get_bootstrap_port(mach_task_self(), &bootstrap_port);
+       if (status != KERN_SUCCESS) {
+               fprintf(stderr, "task_get_bootstrap_port(): %s\n",
+                       mach_error_string(status));
+               exit (EX_UNAVAILABLE);
+       }
+
+       /* Check "configd" server status */
+       status = bootstrap_status(bootstrap_port, SCD_SERVER, &active);
+       switch (status) {
+               case BOOTSTRAP_SUCCESS :
+                       if (active) {
+                               fprintf(stderr, "configd: '%s' server already active\n",
+                                       SCD_SERVER);
+                               return TRUE;
+                       }
+                       break;
+               case BOOTSTRAP_UNKNOWN_SERVICE :
+                       break;
+               default :
+                       fprintf(stderr, "bootstrap_status(): %s\n",
+                               mach_error_string(status));
+                       exit (EX_UNAVAILABLE);
+       }
+       return FALSE;
+}
+
+void
+server_init()
+{
+       kern_return_t           status;
+       mach_port_t             bootstrap_port;
+       boolean_t               active;
+       CFRunLoopSourceRef      rls;
+
+       /* Getting bootstrap server port */
+       status = task_get_bootstrap_port(mach_task_self(), &bootstrap_port);
+       if (status != KERN_SUCCESS) {
+               SCDLog(LOG_DEBUG, CFSTR("task_get_bootstrap_port(): %s"), mach_error_string(status));
+               exit (EX_UNAVAILABLE);
+       }
+
+       /* Check "configd" server status */
+       status = bootstrap_status(bootstrap_port, SCD_SERVER, &active);
+       switch (status) {
+               case BOOTSTRAP_SUCCESS :
+                       if (active) {
+                               SCDLog(LOG_DEBUG, CFSTR("\"%s\" is currently active, exiting."), SCD_SERVER);
+                               exit (EX_UNAVAILABLE);
+                       }
+                       break;
+               case BOOTSTRAP_UNKNOWN_SERVICE :
+                       /* service not currently registered, "a good thing" (tm) */
+                       break;
+               default :
+                       fprintf(stderr, "bootstrap_status(): %s\n", mach_error_string(status));
+                       exit (EX_UNAVAILABLE);
+       }
+
+       /* Create the primary / new connection port */
+       configd_port = CFMachPortCreate(NULL, configdCallback, NULL, NULL);
+
+       /*
+        * Create and add a run loop source for the port and add this source
+        * to both the default run loop mode and the "locked" mode. These two
+        * modes will be used for normal (unlocked) communication with the
+        * server and when multiple (locked) updates are requested by a single
+        * session.
+        */
+       rls = CFMachPortCreateRunLoopSource(NULL, configd_port, 0);
+       CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
+       CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, CFSTR("locked"));
+       CFRelease(rls);
+
+       /* Create a session for the primary / new connection port */
+       (void) addSession(configd_port);
+
+       SCDLog(LOG_DEBUG, CFSTR("Registering service \"%s\""), SCD_SERVER);
+       status = bootstrap_register(bootstrap_port, SCD_SERVER, CFMachPortGetPort(configd_port));
+       switch (status) {
+               case BOOTSTRAP_SUCCESS :
+                       /* service not currently registered, "a good thing" (tm) */
+                       break;
+               case BOOTSTRAP_NOT_PRIVILEGED :
+                       SCDLog(LOG_ERR, CFSTR("bootstrap_register(): bootstrap not privileged"));
+                       exit (EX_OSERR);
+               case BOOTSTRAP_SERVICE_ACTIVE :
+                       SCDLog(LOG_ERR, CFSTR("bootstrap_register(): bootstrap service active"));
+                       exit (EX_OSERR);
+               default :
+                       SCDLog(LOG_ERR, CFSTR("bootstrap_register(): %s"), mach_error_string(status));
+                       exit (EX_OSERR);
+       }
+
+       return;
+}
+
+
+void
+server_loop()
+{
+       CFStringRef     rlMode;
+       int             rlStatus;
+
+       while (TRUE) {
+               boolean_t       isLocked = SCDOptionGet(NULL, kSCDOptionIsLocked);
+
+               /*
+                * if linked with a DEBUG version of the framework, display some
+                * debugging information
+                */
+               _showMachPortStatus();
+
+               /*
+                * process one run loop event
+                */
+               rlMode = isLocked ? CFSTR("locked") : kCFRunLoopDefaultMode;
+               rlStatus = CFRunLoopRunInMode(rlMode, 1.0e10, TRUE);
+
+               /*
+                * check for, and if necessary, push out change notifications
+                * to other processes.
+                */
+               pushNotifications();
+       }
+}
diff --git a/configd.tproj/configd_server.h b/configd.tproj/configd_server.h
new file mode 100644 (file)
index 0000000..bc5708c
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#ifndef _S_CONFIGD_SERVER_H
+#define _S_CONFIGD_SERVER_H
+
+#include <sys/cdefs.h>
+#include <mach/mach.h>
+
+#include <CoreFoundation/CFRunLoop.h>
+#include <CoreFoundation/CFMachPort.h>
+
+extern CFMachPortRef           configd_port;           /* configd server port (for new session requests) */
+
+__BEGIN_DECLS
+
+void           configdCallback __P((CFMachPortRef              port,
+                                    void                       *msg,
+                                    CFIndex                    size,
+                                    void                       *info));
+
+boolean_t      server_active   __P(());
+void           server_init     __P(());
+
+void           server_loop     __P(());
+
+kern_return_t  _snapshot       __P((mach_port_t server, int *scd_status));
+
+kern_return_t  _configopen     __P((mach_port_t                server,
+                                    xmlData_t                  name,
+                                    mach_msg_type_number_t     nameCnt,
+                                    mach_port_t                *newServer,
+                                    int                        *scd_status));
+
+kern_return_t  _configclose    __P((mach_port_t server, int *scd_status));
+
+kern_return_t  _configlock     __P((mach_port_t server, int *scd_status));
+
+kern_return_t  _configunlock   __P((mach_port_t server, int *scd_status));
+
+kern_return_t  _configlist     __P((mach_port_t server,
+                                    xmlData_t                  key,
+                                    mach_msg_type_number_t     keyCnt,
+                                    int                        regexOptions,
+                                    xmlDataOut_t               *list,
+                                    mach_msg_type_number_t     *listCnt,
+                                    int                        *scd_status));
+
+kern_return_t  _configadd      __P((mach_port_t                server,
+                                    xmlData_t                  key,
+                                    mach_msg_type_number_t     keyCnt,
+                                    xmlData_t                  data,
+                                    mach_msg_type_number_t     dataCnt,
+                                    int                        *newInstance,
+                                    int                        *scd_status));
+
+kern_return_t  _configadd_s    __P((mach_port_t                server,
+                                    xmlData_t                  key,
+                                    mach_msg_type_number_t     keyCnt,
+                                    xmlData_t                  data,
+                                    mach_msg_type_number_t     dataCnt,
+                                    int                        *newInstance,
+                                    int                        *scd_status));
+
+kern_return_t  _configget      __P((mach_port_t                server,
+                                    xmlData_t                  key,
+                                    mach_msg_type_number_t     keyCnt,
+                                    xmlDataOut_t               *data,
+                                    mach_msg_type_number_t     *dataCnt,
+                                    int                        *newInstance,
+                                    int                        *scd_status));
+
+kern_return_t  _configset      __P((mach_port_t                server,
+                                    xmlData_t                  key,
+                                    mach_msg_type_number_t     keyCnt,
+                                    xmlData_t                  data,
+                                    mach_msg_type_number_t     dataCnt,
+                                    int                        *newInstance,
+                                    int                        *scd_status));
+
+kern_return_t  _configremove   __P((mach_port_t                server,
+                                    xmlData_t                  key,
+                                    mach_msg_type_number_t     keyCnt,
+                                    int                        *scd_status));
+
+kern_return_t  _configtouch    __P((mach_port_t                server,
+                                    xmlData_t                  key,
+                                    mach_msg_type_number_t     keyCnt,
+                                    int                        *scd_status));
+
+kern_return_t  _notifyadd      __P((mach_port_t                server,
+                                    xmlData_t                  key,
+                                    mach_msg_type_number_t     keyCnt,
+                                    int                        regexOptions,
+                                    int                        *status));
+
+kern_return_t  _notifyremove   __P((mach_port_t                server,
+                                    xmlData_t                  key,
+                                    mach_msg_type_number_t     keyCnt,
+                                    int                        regexOptions,
+                                    int                        *status));
+
+kern_return_t  _notifychanges  __P((mach_port_t                server,
+                                    xmlDataOut_t               *list,
+                                    mach_msg_type_number_t     *listCnt,
+                                    int                        *status));
+
+kern_return_t  _notifyviaport  __P((mach_port_t                server,
+                                    mach_port_t                port,
+                                    mach_msg_id_t              msgid,
+                                    int                        *status));
+
+kern_return_t  _notifyviafd    __P((mach_port_t                server,
+                                    xmlData_t                  path,
+                                    mach_msg_type_number_t     pathCnt,
+                                    int                        identifier,
+                                    int                        *status));
+
+kern_return_t  _notifyviasignal
+                               __P((mach_port_t                server,
+                                    task_t                     task,
+                                    int                        signal,
+                                    int                        *status));
+
+kern_return_t  _notifycancel   __P((mach_port_t                server,
+                                    int                        *scd_status));
+
+__END_DECLS
+
+#endif /* !_S_CONFIGD_SERVER_H */
diff --git a/configd.tproj/h.template b/configd.tproj/h.template
new file mode 100644 (file)
index 0000000..f3c1b04
--- /dev/null
@@ -0,0 +1,11 @@
+$$
+/* $FILENAME$ created by $USERNAME$ on $DATE$ */
+
+#import <Foundation/Foundation.h>
+
+@interface $FILENAMESANSEXTENSION$ : NSObject
+{
+
+}
+
+@end
diff --git a/configd.tproj/m.template b/configd.tproj/m.template
new file mode 100644 (file)
index 0000000..1216fe5
--- /dev/null
@@ -0,0 +1,18 @@
+$$ Lines starting with $$ are not inserted into newly created files
+$$ The following substitutions are made:
+$$
+$$ $FILENAME$                e.g. foo.m
+$$ $FILENAMESANSEXTENSION$   e.g. foo
+$$ $DIRECTORY$               e.g. /tmp/MyNewApp
+$$ $PROJECTNAME$             e.g. MyNewApp
+$$ $SUBPROJECTNAME$          e.g. TheGoodPart.subproj
+$$ $USERNAME$                e.g. mwagner
+$$ $DATE$                    e.g. Jan-1-1994
+$$
+/* $FILENAME$ created by $USERNAME$ on $DATE$ */
+
+#import "$FILENAMESANSEXTENSION$.h"
+
+@implementation $FILENAMESANSEXTENSION$
+
+@end
diff --git a/configd.tproj/notify.c b/configd.tproj/notify.c
new file mode 100644 (file)
index 0000000..b87dd9e
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+
+#include <unistd.h>
+
+#include "configd.h"
+#include "configd_server.h"
+#include "session.h"
+
+
+void
+pushNotifications()
+{
+       void                    **sessionsToNotify;
+       CFIndex                 notifyCnt;
+       int                     server;
+       serverSessionRef        theSession;
+       SCDSessionPrivateRef    sessionPrivate;
+
+       if (needsNotification == NULL)
+               return;         /* if no sessions need to be kicked */
+
+       notifyCnt = CFSetGetCount(needsNotification);
+       sessionsToNotify = malloc(notifyCnt * sizeof(CFNumberRef));
+       CFSetGetValues(needsNotification, sessionsToNotify);
+       while (--notifyCnt >= 0) {
+               (void) CFNumberGetValue(sessionsToNotify[notifyCnt],
+                                       kCFNumberIntType,
+                                       &server);
+               theSession = getSession(server);
+               sessionPrivate = (SCDSessionPrivateRef)theSession->session;
+
+               /*
+                * handle callbacks for "configd" plug-ins
+                */
+               if (sessionPrivate->callbackFunction != NULL) {
+                       SCDLog(LOG_DEBUG, CFSTR("executing notifiction callback function (server=%d)."),
+                              sessionPrivate->server);
+                       (void) (*sessionPrivate->callbackFunction)(theSession->session,
+                                                                  sessionPrivate->callbackArgument);
+               }
+
+               /*
+                * deliver notifications to client sessions
+                */
+               if (sessionPrivate->notifyPort != MACH_PORT_NULL) {
+                       mach_msg_empty_send_t   msg;
+                       mach_msg_option_t       options;
+                       kern_return_t           status;
+                       /*
+                        * Post notification as mach message
+                        */
+                       SCDLog(LOG_DEBUG, CFSTR("sending mach message notification."));
+                       SCDLog(LOG_DEBUG, CFSTR("  port  = %d"), sessionPrivate->notifyPort);
+                       SCDLog(LOG_DEBUG, CFSTR("  msgid = %d"), sessionPrivate->notifyPortIdentifier);
+                       msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
+                       msg.header.msgh_size = sizeof(msg);
+                       msg.header.msgh_remote_port = sessionPrivate->notifyPort;
+                       msg.header.msgh_local_port = MACH_PORT_NULL;
+                       msg.header.msgh_id = sessionPrivate->notifyPortIdentifier;
+                       options = MACH_SEND_TIMEOUT;
+                       status = mach_msg(&msg.header,                  /* msg */
+                                         MACH_SEND_MSG|options,        /* options */
+                                         msg.header.msgh_size,         /* send_size */
+                                         0,                            /* rcv_size */
+                                         MACH_PORT_NULL,               /* rcv_name */
+                                         0,                            /* timeout */
+                                         MACH_PORT_NULL);              /* notify */
+               }
+
+               if (sessionPrivate->notifyFile >= 0) {
+                       ssize_t         written;
+
+                       SCDLog(LOG_DEBUG, CFSTR("sending (UNIX domain) socket notification"));
+                       SCDLog(LOG_DEBUG, CFSTR("  fd    = %d"), sessionPrivate->notifyFile);
+                       SCDLog(LOG_DEBUG, CFSTR("  msgid = %d"), sessionPrivate->notifyFileIdentifier);
+
+                       written = write(sessionPrivate->notifyFile,
+                                       &sessionPrivate->notifyFileIdentifier,
+                                       sizeof(sessionPrivate->notifyFileIdentifier));
+                       if (written == -1) {
+                               if (errno == EWOULDBLOCK) {
+                                       SCDLog(LOG_DEBUG,
+                                              CFSTR("sorry, only one outstanding notification per session."));
+                               } else {
+                                       SCDLog(LOG_DEBUG,
+                                              CFSTR("could not send notification, write() failed: %s"),
+                                              strerror(errno));
+                                       sessionPrivate->notifyFile = -1;
+                               }
+                       } else if (written != sizeof(sessionPrivate->notifyFileIdentifier)) {
+                               SCDLog(LOG_DEBUG,
+                                      CFSTR("could not send notification, incomplete write()"));
+                               sessionPrivate->notifyFile = -1;
+                       }
+               }
+
+               if (sessionPrivate->notifySignal > 0) {
+                       kern_return_t   status;
+                       pid_t           pid;
+                       /*
+                        * Post notification as signal
+                        */
+                       status = pid_for_task(sessionPrivate->notifySignalTask, &pid);
+                       if (status == KERN_SUCCESS) {
+                               SCDLog(LOG_DEBUG, CFSTR("sending signal notification"));
+                               SCDLog(LOG_DEBUG, CFSTR("  pid    = %d"), pid);
+                               SCDLog(LOG_DEBUG, CFSTR("  signal = %d"), sessionPrivate->notifySignal);
+                               if (kill(pid, sessionPrivate->notifySignal) != 0) {
+                                       SCDLog(LOG_DEBUG, CFSTR("could not send signal: %s"), strerror(errno));
+                                       status = KERN_FAILURE;
+                               }
+                       } else {
+                               mach_port_type_t        pt;
+
+                               if ((mach_port_type(mach_task_self(), sessionPrivate->notifySignalTask, &pt) == KERN_SUCCESS) &&
+                                   (pt & MACH_PORT_TYPE_DEAD_NAME)) {
+                                       SCDLog(LOG_DEBUG, CFSTR("could not send signal, process died"));
+                               } else {
+                                       SCDLog(LOG_DEBUG, CFSTR("could not send signal: %s"), mach_error_string(status));
+                               }
+                       }
+
+                       if (status != KERN_SUCCESS) {
+                               /* don't bother with any more attempts */
+                               (void) mach_port_destroy(mach_task_self(), sessionPrivate->notifySignalTask);
+                               sessionPrivate->notifySignal     = 0;
+                               sessionPrivate->notifySignalTask = TASK_NULL;
+                       }
+              }
+       }
+       free(sessionsToNotify);
+
+       /*
+        * this list of notifications have been posted, wait for some more.
+        */
+       CFRelease(needsNotification);
+       needsNotification = NULL;
+
+       return;
+}
diff --git a/configd.tproj/notify.h b/configd.tproj/notify.h
new file mode 100644 (file)
index 0000000..cd1f018
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#ifndef _S_NOTIFY_H
+#define _S_NOTIFY_H
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+void           pushNotifications       __P(());
+
+__END_DECLS
+
+#endif /* !_S_NOTIFY_H */
diff --git a/configd.tproj/notify_server.c b/configd.tproj/notify_server.c
new file mode 100644 (file)
index 0000000..c043d43
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include "configd.h"
+#include "session.h"
+
+boolean_t
+notify_server(mach_msg_header_t *request, mach_msg_header_t *reply)
+{
+       mach_no_senders_notification_t  *notify = (mach_no_senders_notification_t *)request;
+
+       if ((notify->not_header.msgh_id > MACH_NOTIFY_LAST) ||
+           (notify->not_header.msgh_id < MACH_NOTIFY_FIRST)) {
+               return FALSE;   /* if this is not a notification message */
+       }
+
+       switch (notify->not_header.msgh_id) {
+               case MACH_NOTIFY_NO_SENDERS :
+                       SCDLog(LOG_DEBUG, CFSTR("No more senders for port %d, closing."),
+                                notify->not_header.msgh_local_port);
+                       cleanupSession(notify->not_header.msgh_local_port);
+
+                       (void) mach_port_destroy(mach_task_self(), notify->not_header.msgh_local_port);
+
+                       notify->not_header.msgh_remote_port = MACH_PORT_NULL;
+                       return TRUE;
+               case MACH_NOTIFY_DEAD_NAME :
+                       SCDLog(LOG_DEBUG, CFSTR("Dead name for port %d, closing."),
+                                notify->not_header.msgh_local_port);
+                       cleanupSession(notify->not_header.msgh_local_port);
+
+                       (void) mach_port_destroy(mach_task_self(), notify->not_header.msgh_local_port);
+
+                       notify->not_header.msgh_remote_port = MACH_PORT_NULL;
+                       return TRUE;
+               default :
+                       break;
+       }
+
+       SCDLog(LOG_DEBUG, CFSTR("HELP!, Received notification: port=%d, msgh_id=%d"),
+              notify->not_header.msgh_local_port,
+              notify->not_header.msgh_id);
+
+       return FALSE;
+}
diff --git a/configd.tproj/notify_server.h b/configd.tproj/notify_server.h
new file mode 100644 (file)
index 0000000..170d718
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#ifndef _S_NOTIFY_SERVER_H
+#define _S_NOTIFY_SERVER_H
+
+#include <sys/cdefs.h>
+#include <mach/mach.h>
+
+__BEGIN_DECLS
+
+boolean_t      notify_server   __P((mach_msg_header_t *request, mach_msg_header_t *reply));
+
+__END_DECLS
+
+#endif /* !_S_NOTIFY_SERVER_H */
diff --git a/configd.tproj/plugin_support.c b/configd.tproj/plugin_support.c
new file mode 100644 (file)
index 0000000..fd6e3c8
--- /dev/null
@@ -0,0 +1,568 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include <mach-o/dyld.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <NSSystemDirectories.h>
+
+#include "configd.h"
+
+
+/*
+ * Information maintained for each to-be-kicked registration.
+ */
+typedef struct {
+       /*
+        * bundle paths
+        */
+       char                            bundle[MAXNAMLEN + 1];  /* bundle name */
+       char                            path  [MAXPATHLEN];     /* bundle path */
+
+       /*
+        * entry points for initialization code.
+        */
+       SCDBundleStartRoutine_t         start;  /* address of start() routine */
+       SCDBundlePrimeRoutine_t         prime;  /* address of prime() routine */
+
+} plugin, *pluginRef;
+
+CFMutableArrayRef      plugins;
+
+
+/* exception handling functions */
+typedef kern_return_t (*cer_func_t)            (mach_port_t            exception_port,
+                                                mach_port_t            thread,
+                                                mach_port_t            task,
+                                                exception_type_t       exception,
+                                                exception_data_t       code,
+                                                mach_msg_type_number_t codeCnt);
+
+typedef kern_return_t (*cer_state_func_t)      (mach_port_t            exception_port,
+                                                exception_type_t       exception,
+                                                exception_data_t       code,
+                                                mach_msg_type_number_t codeCnt,
+                                                int                    *flavor,
+                                                thread_state_t         old_state,
+                                                mach_msg_type_number_t old_stateCnt,
+                                                thread_state_t         new_state,
+                                                mach_msg_type_number_t *new_stateCnt);
+
+typedef kern_return_t (*cer_identity_func_t)   (mach_port_t            exception_port,
+                                                mach_port_t            thread,
+                                                mach_port_t            task,
+                                                exception_type_t       exception,
+                                                exception_data_t       code,
+                                                mach_msg_type_number_t codeCnt,
+                                                int                    *flavor,
+                                                thread_state_t         old_state,
+                                                mach_msg_type_number_t old_stateCnt,
+                                                thread_state_t         new_state,
+                                                mach_msg_type_number_t *new_stateCnt);
+
+static cer_func_t              catch_exception_raise_func          = NULL;
+static cer_state_func_t                catch_exception_raise_state_func    = NULL;
+static cer_identity_func_t     catch_exception_raise_identity_func = NULL;
+
+kern_return_t
+catch_exception_raise(mach_port_t              exception_port,
+                     mach_port_t               thread,
+                     mach_port_t               task,
+                     exception_type_t          exception,
+                     exception_data_t          code,
+                     mach_msg_type_number_t    codeCnt)
+{
+
+       if (catch_exception_raise_func == NULL) {
+               /* The user hasn't defined catch_exception_raise in their binary */
+               abort();
+       }
+       return (*catch_exception_raise_func)(exception_port,
+                                            thread,
+                                            task,
+                                            exception,
+                                            code,
+                                            codeCnt);
+}
+
+
+kern_return_t
+catch_exception_raise_state(mach_port_t                        exception_port,
+                           exception_type_t            exception,
+                           exception_data_t            code,
+                           mach_msg_type_number_t      codeCnt,
+                           int                         *flavor,
+                           thread_state_t              old_state,
+                           mach_msg_type_number_t      old_stateCnt,
+                           thread_state_t              new_state,
+                           mach_msg_type_number_t      *new_stateCnt)
+{
+       if (catch_exception_raise_state_func == 0) {
+               /* The user hasn't defined catch_exception_raise_state in their binary */
+               abort();
+       }
+       return (*catch_exception_raise_state_func)(exception_port,
+                                                  exception,
+                                                  code,
+                                                  codeCnt,
+                                                  flavor,
+                                                  old_state,
+                                                  old_stateCnt,
+                                                  new_state,
+                                                  new_stateCnt);
+}
+
+
+kern_return_t
+catch_exception_raise_state_identity(mach_port_t               exception_port,
+                                    mach_port_t                thread,
+                                    mach_port_t                task,
+                                    exception_type_t           exception,
+                                    exception_data_t           code,
+                                    mach_msg_type_number_t     codeCnt,
+                                    int                        *flavor,
+                                    thread_state_t             old_state,
+                                    mach_msg_type_number_t     old_stateCnt,
+                                    thread_state_t             new_state,
+                                    mach_msg_type_number_t     *new_stateCnt)
+{
+       if (catch_exception_raise_identity_func == 0) {
+               /* The user hasn't defined catch_exception_raise_identify in their binary */
+               abort();
+       }
+       return (*catch_exception_raise_identity_func)(exception_port,
+                                                     thread,
+                                                     task,
+                                                     exception,
+                                                     code,
+                                                     codeCnt,
+                                                     flavor,
+                                                     old_state,
+                                                     old_stateCnt,
+                                                     new_state,
+                                                     new_stateCnt);
+}
+
+
+static boolean_t
+bundleLoad(pluginRef info)
+{
+       int                             len;
+       NSObjectFileImage               image;
+       NSObjectFileImageReturnCode     status;
+       NSModule                        module;
+       NSSymbol                        symbol;
+       unsigned long                   options;
+       char                            *bundleExe;     /* full path of bundle executable */
+       struct stat                     sb;
+
+       /*
+        * allocate enough space for the bundle directory path, a "/" separator,
+        * the bundle name, and the (optional) "_debug" extension.
+        */
+
+       len =  strlen(info->path);              /* path */
+       len += sizeof(BUNDLE_NEW_SUBDIR) - 1;   /* "/" or "/Contents/MacOS/" */
+       len += strlen(info->bundle);            /* bundle name */
+       len += sizeof(BUNDLE_DEBUG_EXTENSION);  /* "_debug" (and NUL) */
+       bundleExe = CFAllocatorAllocate(NULL, len, 0);
+
+       /* check for the (old layout) bundle executable path */
+       strcpy(bundleExe, info->path);
+       strcat(bundleExe, BUNDLE_OLD_SUBDIR);
+       strcat(bundleExe, info->bundle);
+       if (stat(bundleExe, &sb) == 0) {
+               goto load;
+       }
+
+       /* check for the "_debug" version */
+       strcat(bundleExe, BUNDLE_DEBUG_EXTENSION);
+       if (stat(bundleExe, &sb) == 0) {
+               goto load;
+       }
+
+       /* check for the (new layout) bundle executable path */
+       strcpy(bundleExe, info->path);
+       strcat(bundleExe, BUNDLE_NEW_SUBDIR);
+       strcat(bundleExe, info->bundle);
+       if (stat(bundleExe, &sb) == 0) {
+               goto load;
+       }
+
+       /* check for the "_debug" version */
+       strcat(bundleExe, BUNDLE_DEBUG_EXTENSION);
+       if (stat(bundleExe, &sb) == 0) {
+               goto load;
+       }
+
+       SCDLog(LOG_ERR,
+              CFSTR("bundleLoad() failed, no executable for %s in %s"),
+              info->bundle,
+              info->path);
+       CFAllocatorDeallocate(NULL, bundleExe);
+       return FALSE;
+
+    load :
+
+       /* load the bundle */
+       SCDLog(LOG_DEBUG, CFSTR("loading %s"), bundleExe);
+       status = NSCreateObjectFileImageFromFile(bundleExe, &image);
+       if (status != NSObjectFileImageSuccess) {
+               char    *err;
+
+               switch (status) {
+                       case NSObjectFileImageFailure :
+                               err = "NSObjectFileImageFailure";
+                               break;
+                       case NSObjectFileImageInappropriateFile :
+                               err = "NSObjectFileImageInappropriateFile";
+                               break;
+                       case NSObjectFileImageArch :
+                               err = "NSObjectFileImageArch";
+                               break;
+                       case NSObjectFileImageFormat :
+                               err = "NSObjectFileImageFormat";
+                               break;
+                       case NSObjectFileImageAccess :
+                               err = "NSObjectFileImageAccess";
+                               break;
+                       default :
+                               err = "Unknown";
+                               break;
+               }
+               SCDLog(LOG_ERR, CFSTR("NSCreateObjectFileImageFromFile() failed"));
+               SCDLog(LOG_ERR, CFSTR("  executable path = %s"), bundleExe);
+               SCDLog(LOG_ERR, CFSTR("  error status    = %s"), err);
+               CFAllocatorDeallocate(NULL, bundleExe);
+               return FALSE;
+       }
+
+       options =  NSLINKMODULE_OPTION_BINDNOW;
+       options |= NSLINKMODULE_OPTION_PRIVATE;
+       options |= NSLINKMODULE_OPTION_RETURN_ON_ERROR;
+       module = NSLinkModule(image, bundleExe, options);
+
+       if (module == NULL) {
+               NSLinkEditErrors        c;
+               int                     errorNumber;
+               const char              *fileName;
+               const char              *errorString;
+
+               SCDLog(LOG_ERR, CFSTR("NSLinkModule() failed"));
+               SCDLog(LOG_ERR, CFSTR("  executable path = %s"), bundleExe);
+
+               /* collect and report the details */
+               NSLinkEditError(&c, &errorNumber, &fileName, &errorString);
+               SCDLog(LOG_ERR, CFSTR("  NSLinkEditErrors = %d"), (int)c);
+               SCDLog(LOG_ERR, CFSTR("  errorNumber      = %d"), errorNumber);
+               if((fileName != NULL) && (*fileName != '\0'))
+                       SCDLog(LOG_ERR, CFSTR("  fileName         = %s"), fileName);
+               if((errorString != NULL) && (*errorString != '\0'))
+                       SCDLog(LOG_ERR, CFSTR("  errorString      = %s"), errorString);
+
+               CFAllocatorDeallocate(NULL, bundleExe);
+               return FALSE;
+       }
+
+       CFAllocatorDeallocate(NULL, bundleExe);
+
+       /* identify the initialization functions */
+
+       symbol = NSLookupSymbolInModule(module, BUNDLE_ENTRY_POINT);
+       if (symbol) {
+               info->start = NSAddressOfSymbol(symbol);
+       }
+
+       symbol = NSLookupSymbolInModule(module, BUNDLE_ENTRY_POINT2);
+       if (symbol) {
+               info->prime = NSAddressOfSymbol(symbol);
+       }
+
+       if ((info->start == NULL) && (info->prime == NULL)) {
+               SCDLog(LOG_DEBUG, CFSTR("  no entry points"));
+               return FALSE;
+       }
+
+       /* identify any exception handling functions */
+
+       symbol = NSLookupSymbolInModule(module, "_catch_exception_raise");
+       if (symbol) {
+               catch_exception_raise_func = NSAddressOfSymbol(symbol);
+       }
+
+       symbol = NSLookupSymbolInModule(module, "_catch_exception_raise_state");
+       if (symbol) {
+               catch_exception_raise_state_func = NSAddressOfSymbol(symbol);
+       }
+
+       symbol = NSLookupSymbolInModule(module, "_catch_exception_raise_identity");
+       if (symbol) {
+               catch_exception_raise_identity_func = NSAddressOfSymbol(symbol);
+       }
+
+       return TRUE;
+}
+
+
+static void
+bundleStart(const void *value, void *context)
+{
+       CFDataRef       data = (CFDataRef)value;
+       pluginRef       info;
+
+       info = (pluginRef)CFDataGetBytePtr(data);
+       if (info->start) {
+               (*info->start)(info->bundle, info->path);
+       }
+}
+
+
+static void
+bundlePrime(const void *value, void *context)
+{
+       CFDataRef       data = (CFDataRef)value;
+       pluginRef       info;
+
+       info = (pluginRef)CFDataGetBytePtr(data);
+       if (info->prime) {
+               (*info->prime)(info->bundle, info->path);
+       }
+}
+
+
+static void
+loadOne(const char *bundleDir, const char *bundleName)
+{
+       CFMutableDataRef        info;
+       pluginRef               pluginInfo;
+       int                     len;
+
+       /* check if this directory entry is a valid bundle name */
+       len = strlen(bundleName);
+       if (len <= sizeof(BUNDLE_DIR_EXTENSION)) {
+               /* if entry name isn't long enough */
+               return;
+       }
+
+       len -= sizeof(BUNDLE_DIR_EXTENSION) - 1;
+       if (strcmp(&bundleName[len], BUNDLE_DIR_EXTENSION) != 0) {
+               /* if entry name doesn end with ".bundle" */
+               return;
+       }
+
+       info = CFDataCreateMutable(NULL, sizeof(plugin));
+       pluginInfo = (pluginRef)CFDataGetBytePtr(info);
+       pluginInfo->start = NULL;
+       pluginInfo->prime = NULL;
+
+       /* get (just) the bundle's name */
+       pluginInfo->bundle[0] = '\0';
+       (void) strncat(pluginInfo->bundle, bundleName, len);
+
+       /* get the bundle directory path */
+       (void) sprintf(pluginInfo->path, "%s/%s", bundleDir, bundleName);
+
+       /* load the bundle */
+       if (bundleLoad(pluginInfo)) {
+               SCDLog(LOG_INFO, CFSTR("%s loaded"), bundleName);
+               CFArrayAppendValue(plugins, info);
+       } else {
+               SCDLog(LOG_ERR,  CFSTR("load of \"%s\" failed"), bundleName);
+       }
+       CFRelease(info);
+
+       return;
+}
+
+
+static void
+loadAll(const char *bundleDir)
+{
+       DIR                     *dirp;
+       struct dirent           *dp;
+
+       dirp = opendir(bundleDir);
+       if (dirp == NULL) {
+               /* if no plugin directory */
+               return;
+       }
+
+       while ((dp = readdir(dirp)) != NULL) {
+               loadOne(bundleDir, dp->d_name);
+       }
+
+       closedir(dirp);
+       return;
+}
+
+
+void
+timerCallback(CFRunLoopTimerRef timer, void *info)
+{
+       SCDLog(LOG_INFO, CFSTR("the CFRunLoop is waiting for something to happen...."));
+       return;
+}
+
+
+void *
+plugin_exec(void *arg)
+{
+       NSSearchPathEnumerationState    state;
+       char                            path[MAXPATHLEN];
+
+       /* keep track of loaded plugins */
+       plugins = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+
+       if (arg == NULL) {
+               /*
+                * identify and load all plugins
+                */
+               state = NSStartSearchPathEnumeration(NSLibraryDirectory,
+                                                    NSLocalDomainMask|NSSystemDomainMask);
+               while ((state = NSGetNextSearchPathEnumeration(state, path))) {
+                       /* load any available plugins */
+                       strcat(path, BUNDLE_DIRECTORY);
+                       SCDLog(LOG_DEBUG, CFSTR("searching for plugins in \"%s\""), path);
+                       loadAll(path);
+               }
+
+               if (SCDOptionGet(NULL, kSCDOptionDebug)) {
+                       SCDLog(LOG_DEBUG, CFSTR("searching for plugins in \".\""));
+                       loadAll(".");
+               }
+       } else {
+               /*
+                * load the plugin specified on the command line
+                */
+               char    *bn, *bd;
+
+               if ((bn = strrchr((char *)arg, '/')) != NULL) {
+                       int     len;
+
+                       /* plug-in directory */
+                       len = bn - (char *)arg;
+                       if (len == 0)
+                               len++;          /* if plugin is in the root directory */
+
+                       bd = CFAllocatorAllocate(NULL, len + 1, 0);
+                       bd[0] = '\0';
+                       (void) strncat(bd, (char *)arg, len);
+
+                       /* plug-in name */
+                       bn++;           /* name starts just after trailing path separator */
+               } else {
+                       /* plug-in (in current) directory */
+                       bd = CFAllocatorAllocate(NULL, sizeof("."), 0);
+                       (void) strcpy(bd, ".");
+
+                       /* plug-in name */
+                       bn = (char *)arg;       /* no path separators */
+               }
+
+               loadOne(bd, bn);
+
+               CFAllocatorDeallocate(NULL, bd);
+
+               /* allocate a periodic event (to help show we're not blocking) */
+               if (CFArrayGetCount(plugins)) {
+                       CFRunLoopTimerRef       timer;
+
+                       timer = CFRunLoopTimerCreate(NULL,                              /* allocator */
+                                                    CFAbsoluteTimeGetCurrent() + 1.0,  /* fireDate */
+                                                    60.0,                              /* interval */
+                                                    0,                                 /* flags */
+                                                    0,                                 /* order */
+                                                    timerCallback,                     /* callout */
+                                                    NULL);                             /* context */
+                       CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode);
+                       CFRelease(timer);
+               }
+       }
+
+       /*
+        * execute each plugins start() function which should initialize any
+        * variables, open any sessions with "configd", and register any needed
+        * notifications. Establishing initial information in the cache should
+        * be deferred until the prime() initialization function so that any
+        * plug-ins which want to receive a notification that the data has
+        * changed will have an opportunity to install a notification handler.
+        */
+       SCDLog(LOG_DEBUG, CFSTR("calling plugin start() functions"));
+       CFArrayApplyFunction(plugins,
+                            CFRangeMake(0, CFArrayGetCount(plugins)),
+                            bundleStart,
+                            NULL);
+
+       /*
+        * execute each plugins prime() function which should initialize any
+        * configuration information and/or state in the cache.
+        */
+       SCDLog(LOG_DEBUG, CFSTR("calling plugin prime() functions"));
+       CFArrayApplyFunction(plugins,
+                            CFRangeMake(0, CFArrayGetCount(plugins)),
+                            bundlePrime,
+                            NULL);
+
+       /*
+        * all plugins have been loaded and started.
+        */
+       CFRelease(plugins);
+
+       if (!SCDOptionGet(NULL, kSCDOptionDebug) && (arg == NULL)) {
+           /* synchronize with parent process */
+           kill(getppid(), SIGTERM);
+       }
+
+       /*
+        * The assumption is that each loaded plugin will establish CFMachPortRef,
+        * CFSocketRef, and CFRunLoopTimerRef input sources to handle any events
+        * and register these sources with this threads run loop. If the plugin
+        * needs to wait and/or block at any time it should do so only in its a
+        * private thread.
+        */
+       SCDLog(LOG_DEBUG, CFSTR("starting plugin CFRunLoop"));
+       CFRunLoopRun();
+       SCDLog(LOG_INFO, CFSTR("what, no more work for the \"configd\" plugins?"));
+       return NULL;
+}
+
+
+void
+plugin_init()
+{
+       pthread_attr_t  tattr;
+       pthread_t       tid;
+
+       SCDLog(LOG_DEBUG, CFSTR("Starting thread for plug-ins..."));
+       pthread_attr_init(&tattr);
+       pthread_attr_setscope(&tattr, PTHREAD_SCOPE_SYSTEM);
+       pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
+//      pthread_attr_setstacksize(&tattr, 96 * 1024); // each thread gets a 96K stack
+       pthread_create(&tid, &tattr, plugin_exec, NULL);
+       pthread_attr_destroy(&tattr);
+       SCDLog(LOG_DEBUG, CFSTR("  thread id=0x%08x"), tid);
+
+       return;
+}
diff --git a/configd.tproj/plugin_support.h b/configd.tproj/plugin_support.h
new file mode 100644 (file)
index 0000000..072e3c1
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#ifndef _S_PLUGIN_SUPPORT_H
+#define _S_PLUGIN_SUPPORT_H
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+void   plugin_init     __P(());
+void   plugin_exec     __P((void       *arg));
+
+__END_DECLS
+
+#endif /* !_S_PLUGIN_SUPPORT_H */
diff --git a/configd.tproj/session.c b/configd.tproj/session.c
new file mode 100644 (file)
index 0000000..88577cd
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include "configd.h"
+#include "configd_server.h"
+#include "session.h"
+
+/* information maintained for each active session */
+static serverSessionRef        *sessions = NULL;
+static int             nSessions = 0;
+
+
+serverSessionRef
+getSession(mach_port_t server)
+{
+       int     i;
+
+       if (server == MACH_PORT_NULL) {
+               SCDLog(LOG_NOTICE, CFSTR("Excuse me, why is getSession() being called with an invalid port?"));
+               return NULL;
+       }
+
+       if (nSessions > 0) {
+               for (i=0; i<nSessions; i++) {
+                       serverSessionRef        thisSession = sessions[i];
+
+                       if (thisSession == NULL) {
+                               /* found an empty slot, skip it */
+                               continue;
+                       } else if (thisSession->key == server) {
+                               return thisSession;     /* we've seen this server before */
+                       } else if ((thisSession->session != NULL) &&
+                                  (((SCDSessionPrivateRef)thisSession->session)->notifySignalTask == server)) {
+                                       return thisSession;
+                       }
+               }
+       }
+
+       /* no sessions available */
+       return NULL;
+}
+
+
+serverSessionRef
+addSession(CFMachPortRef server)
+{
+       int     i;
+       int     n = -1;
+
+       if (nSessions <= 0) {
+               /* new session (actually, the first) found */
+               sessions = malloc(sizeof(serverSessionRef));
+               n = 0;
+               nSessions = 1;
+       } else {
+               for (i=0; i<nSessions; i++) {
+                       if (sessions[i] == NULL) {
+                               /* found an empty slot, use it */
+                               n = i;
+                       }
+               }
+               /* new session identified */
+               if (n < 0) {
+                       /* no empty slots, add one to the list */
+                       n = nSessions++;
+                       sessions = realloc(sessions, ((nSessions) * sizeof(serverSessionRef)));
+               }
+       }
+
+       SCDLog(LOG_DEBUG, CFSTR("Allocating new session for port %d"), CFMachPortGetPort(server));
+       sessions[n] = malloc(sizeof(serverSession));
+       sessions[n]->key                 = CFMachPortGetPort(server);
+       sessions[n]->serverPort          = server;
+       sessions[n]->serverRunLoopSource = NULL;
+       sessions[n]->session             = NULL;
+       sessions[n]->callerEUID          = 1;           /* not "root" */
+       sessions[n]->callerEGID          = 1;           /* not "wheel" */
+
+       return sessions[n];
+}
+
+
+void
+removeSession(mach_port_t server)
+{
+       int                     i;
+       serverSessionRef        thisSession;
+       CFStringRef             sessionKey;
+
+       for (i=0; i<nSessions; i++) {
+               thisSession = sessions[i];
+
+               if (thisSession == NULL) {
+                       /* found an empty slot, skip it */
+                       continue;
+               } else if (thisSession->key == server) {
+                       /*
+                        * We don't need any remaining information in the
+                        * sessionData dictionary, remove it.
+                        */
+                       sessionKey = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), server);
+                       CFDictionaryRemoveValue(sessionData, sessionKey);
+                       CFRelease(sessionKey);
+
+                       /*
+                        * Lastly, get rid of the per-session structure.
+                        */
+                       free(thisSession);
+                       sessions[i] = NULL;
+
+                       return;
+               }
+       }
+
+       return;
+}
+
+
+void
+cleanupSession(mach_port_t server)
+{
+       int             i;
+
+       for (i=0; i<nSessions; i++) {
+               serverSessionRef        thisSession = sessions[i];
+
+               if ((thisSession != NULL) && (thisSession->key == server)) {
+                       /*
+                        * session entry still exists.
+                        */
+
+                       /*
+                        * Ensure that any changes made while we held the "lock"
+                        * are discarded.
+                        */
+                       if (SCDOptionGet(NULL, kSCDOptionIsLocked) &&
+                           SCDOptionGet(thisSession->session, kSCDOptionIsLocked)) {
+                               /*
+                                * swap cache and associated data which, after
+                                * being closed, will result in the restoration
+                                * of the original pre-"locked" data.
+                                */
+                               _swapLockedCacheData();
+                       }
+
+                       /*
+                        * Close any open connections including cancelling any outstanding
+                        * notification requests and releasing any locks.
+                        */
+                       (void) _SCDClose(&thisSession->session);
+
+                       /*
+                        * Lastly, remove the session entry.
+                        */
+                       removeSession(server);
+
+                       return;
+               }
+       }
+       return;
+}
+
+
+void
+listSessions()
+{
+       int     i;
+
+       fprintf(stderr, "Current sessions:");
+       for (i=0; i<nSessions; i++) {
+               serverSessionRef        thisSession = sessions[i];
+
+               if (thisSession == NULL) {
+                       continue;
+               }
+
+               fprintf(stderr, " %d", thisSession->key);
+
+               if (thisSession->session != NULL) {
+                       task_t  task = ((SCDSessionPrivateRef)thisSession->session)->notifySignalTask;
+
+                       if (task != TASK_NULL) {
+                              fprintf(stderr, "/%d", task);
+                       }
+               }
+       }
+       fprintf(stderr, "\n");
+}
+
diff --git a/configd.tproj/session.h b/configd.tproj/session.h
new file mode 100644 (file)
index 0000000..9d431de
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#ifndef _S_SESSION_H
+#define _S_SESSION_H
+
+#include <sys/cdefs.h>
+
+/* Per client server state */
+typedef struct {
+
+       /* mach port used as the key to this session */
+       mach_port_t             key;
+
+       /* mach port associated with this session */
+       CFMachPortRef           serverPort;
+       CFRunLoopSourceRef      serverRunLoopSource;    /* XXX CFMachPortInvalidate() doesn't work */
+
+       /* data associated with this "open" session */
+       SCDSessionRef           session;
+
+       /* credentials associated with this "open" session */
+       int                     callerEUID;
+       int                     callerEGID;
+
+} serverSession, *serverSessionRef;
+
+__BEGIN_DECLS
+
+serverSessionRef       getSession      __P((mach_port_t server));
+
+serverSessionRef       addSession      __P((CFMachPortRef server));
+
+void                   removeSession   __P((mach_port_t server));
+
+void                   cleanupSession  __P((mach_port_t server));
+
+void                   listSessions    __P(());
+
+__END_DECLS
+
+#endif /* !_S_SESSION_H */
diff --git a/scselect.tproj/Makefile b/scselect.tproj/Makefile
new file mode 100644 (file)
index 0000000..64e8cfc
--- /dev/null
@@ -0,0 +1,50 @@
+#
+# Generated by the Apple Project Builder.
+#
+# NOTE: Do NOT change this file -- Project Builder maintains it.
+#
+# Put all of your customizations in files called Makefile.preamble
+# and Makefile.postamble (both optional), and Makefile will include them.
+#
+
+NAME = scselect
+
+PROJECTVERSION = 2.8
+PROJECT_TYPE = Tool
+
+CFILES = scselect.c
+
+OTHERSRCS = Makefile.preamble Makefile Makefile.postamble m.template\
+            h.template
+
+
+MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles
+CODE_GEN_STYLE = DYNAMIC
+MAKEFILE = tool.make
+NEXTSTEP_INSTALLDIR = /usr/sbin
+WINDOWS_INSTALLDIR = /Library/Executables
+PDO_UNIX_INSTALLDIR = /bin
+LIBS = 
+DEBUG_LIBS = $(LIBS)
+PROF_LIBS = $(LIBS)
+
+
+FRAMEWORKS = -framework SystemConfiguration
+
+
+NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc
+WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc
+PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc
+NEXTSTEP_JAVA_COMPILER = /usr/bin/javac
+WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe
+PDO_UNIX_JAVA_COMPILER = $(JDKBINDIR)/javac
+
+include $(MAKEFILEDIR)/platform.make
+
+-include Makefile.preamble
+
+include $(MAKEFILEDIR)/$(MAKEFILE)
+
+-include Makefile.postamble
+
+-include Makefile.dependencies
diff --git a/scselect.tproj/Makefile.postamble b/scselect.tproj/Makefile.postamble
new file mode 100644 (file)
index 0000000..e6f3e09
--- /dev/null
@@ -0,0 +1,100 @@
+###############################################################################
+#  Makefile.postamble
+#  Copyright 1997, Apple Computer, Inc.
+#
+#  Use this makefile, which is imported after all other makefiles, to
+#  override attributes for a project's Makefile environment. This allows you  
+#  to take advantage of the environment set up by the other Makefiles. 
+#  You can also define custom rules at the end of this file.
+#
+###############################################################################
+# 
+# These variables are exported by the standard makefiles and can be 
+# used in any customizations you make.  They are *outputs* of
+# the Makefiles and should be used, not set.
+# 
+#  PRODUCTS: products to install.  All of these products will be placed in
+#       the directory $(DSTROOT)$(INSTALLDIR)
+#  GLOBAL_RESOURCE_DIR: The directory to which resources are copied.
+#  LOCAL_RESOURCE_DIR: The directory to which localized resources are copied.
+#  OFILE_DIR: Directory into which .o object files are generated.
+#  DERIVED_SRC_DIR: Directory used for all other derived files
+#
+#  ALL_CFLAGS:  flags to pass when compiling .c files
+#  ALL_MFLAGS:  flags to pass when compiling .m files
+#  ALL_CCFLAGS:  flags to pass when compiling .cc, .cxx, and .C files
+#  ALL_MMFLAGS:  flags to pass when compiling .mm, .mxx, and .M files
+#  ALL_PRECOMPFLAGS:  flags to pass when precompiling .h files
+#  ALL_LDFLAGS:  flags to pass when linking object files
+#  ALL_LIBTOOL_FLAGS:  flags to pass when libtooling object files
+#  ALL_PSWFLAGS:  flags to pass when processing .psw and .pswm (pswrap) files
+#  ALL_RPCFLAGS:  flags to pass when processing .rpc (rpcgen) files
+#  ALL_YFLAGS:  flags to pass when processing .y (yacc) files
+#  ALL_LFLAGS:  flags to pass when processing .l (lex) files
+#
+#  NAME: name of application, bundle, subproject, palette, etc.
+#  LANGUAGES: langages in which the project is written (default "English")
+#  English_RESOURCES: localized resources (e.g. nib's, images) of project
+#  GLOBAL_RESOURCES: non-localized resources of project
+#
+#  SRCROOT:  base directory in which to place the new source files
+#  SRCPATH:  relative path from SRCROOT to present subdirectory
+#
+#  INSTALLDIR: Directory the product will be installed into by 'install' target
+#  PUBLIC_HDR_INSTALLDIR: where to install public headers.  Don't forget
+#        to prefix this with DSTROOT when you use it.
+#  PRIVATE_HDR_INSTALLDIR: where to install private headers.  Don't forget
+#       to prefix this with DSTROOT when you use it.
+#
+#  EXECUTABLE_EXT: Executable extension for the platform (i.e. .exe on Windows)
+#
+###############################################################################
+
+# Some compiler flags can be overridden here for certain build situations.
+#
+#    WARNING_CFLAGS:  flag used to set warning level (defaults to -Wmost)
+#    DEBUG_SYMBOLS_CFLAGS:  debug-symbol flag passed to all builds (defaults
+#      to -g)
+#    DEBUG_BUILD_CFLAGS:  flags passed during debug builds (defaults to -DDEBUG)
+#    OPTIMIZE_BUILD_CFLAGS:  flags passed during optimized builds (defaults
+#      to -O)
+#    PROFILE_BUILD_CFLAGS:  flags passed during profile builds (defaults
+#      to -pg -DPROFILE)
+#    LOCAL_DIR_INCLUDE_DIRECTIVE:  flag used to add current directory to
+#      the include path (defaults to -I.)
+#    DEBUG_BUILD_LDFLAGS, OPTIMIZE_BUILD_LDFLAGS, PROFILE_BUILD_LDFLAGS: flags
+#      passed to ld/libtool (defaults to nothing)
+
+
+# Library and Framework projects only:
+#    INSTALL_NAME_DIRECTIVE:  This directive ensures that executables linked
+#      against the framework will run against the correct version even if
+#      the current version of the framework changes.  You may override this
+#      to "" as an alternative to using the DYLD_LIBRARY_PATH during your
+#      development cycle, but be sure to restore it before installing.
+
+
+# Ownership and permissions of files installed by 'install' target
+
+#INSTALL_AS_USER = root
+        # User/group ownership 
+#INSTALL_AS_GROUP = wheel
+        # (probably want to set both of these) 
+INSTALL_PERMISSIONS = 4555
+        # If set, 'install' chmod's executable to this
+
+
+# Options to strip.  Note: -S strips debugging symbols (executables can be stripped
+# down further with -x or, if they load no bundles, with no options at all).
+
+#STRIPFLAGS = -S
+
+
+#########################################################################
+# Put rules to extend the behavior of the standard Makefiles here.  Include them in
+# the dependency tree via cvariables like AFTER_INSTALL in the Makefile.preamble.
+#
+# You should avoid redefining things like "install" or "app", as they are
+# owned by the top-level Makefile API and no context has been set up for where 
+# derived files should go.
+#
diff --git a/scselect.tproj/Makefile.preamble b/scselect.tproj/Makefile.preamble
new file mode 100644 (file)
index 0000000..13efa5f
--- /dev/null
@@ -0,0 +1,137 @@
+###############################################################################
+#  Makefile.preamble
+#  Copyright 1997, Apple Computer, Inc.
+#
+#  Use this makefile for configuring the standard application makefiles 
+#  associated with ProjectBuilder. It is included before the main makefile.
+#  In Makefile.preamble you set attributes for a project, so they are available
+#  to the project's makefiles.  In contrast, you typically write additional rules or 
+#  override built-in behavior in the Makefile.postamble.
+#  
+#  Each directory in a project tree (main project plus subprojects) should 
+#  have its own Makefile.preamble and Makefile.postamble.
+###############################################################################
+#
+# Before the main makefile is included for this project, you may set:
+#
+#    MAKEFILEDIR: Directory in which to find $(MAKEFILE)
+#    MAKEFILE: Top level mechanism Makefile (e.g., app.make, bundle.make)
+
+# Compiler/linker flags added to the defaults:  The OTHER_* variables will be 
+# inherited by all nested sub-projects, but the LOCAL_ versions of the same
+# variables will not.  Put your -I, -D, -U, and -L flags in ProjectBuilder's
+# Build Attributes inspector if at all possible.  To override the default flags
+# that get passed to ${CC} (e.g. change -O to -O2), see Makefile.postamble.  The
+# variables below are *inputs* to the build process and distinct from the override
+# settings done (less often) in the Makefile.postamble.
+#
+#    OTHER_CFLAGS, LOCAL_CFLAGS:  additional flags to pass to the compiler
+#      Note that $(OTHER_CFLAGS) and $(LOCAL_CFLAGS) are used for .h, ...c, .m,
+#      .cc, .cxx, .C, and .M files.  There is no need to respecify the
+#      flags in OTHER_MFLAGS, etc.
+#    OTHER_MFLAGS, LOCAL_MFLAGS:  additional flags for .m files
+#    OTHER_CCFLAGS, LOCAL_CCFLAGS:  additional flags for .cc, .cxx, and ...C files
+#    OTHER_MMFLAGS, LOCAL_MMFLAGS:  additional flags for .mm and .M files
+#    OTHER_PRECOMPFLAGS, LOCAL_PRECOMPFLAGS:  additional flags used when
+#      precompiling header files
+#    OTHER_LDFLAGS, LOCAL_LDFLAGS:  additional flags passed to ld and libtool
+#    OTHER_PSWFLAGS, LOCAL_PSWFLAGS:  additional flags passed to pswrap
+#    OTHER_RPCFLAGS, LOCAL_RPCFLAGS:  additional flags passed to rpcgen
+#    OTHER_YFLAGS, LOCAL_YFLAGS:  additional flags passed to yacc
+#    OTHER_LFLAGS, LOCAL_LFLAGS:  additional flags passed to lex
+
+# These variables provide hooks enabling you to add behavior at almost every 
+# stage of the make:
+#
+#    BEFORE_PREBUILD: targets to build before installing headers for a subproject
+#    AFTER_PREBUILD: targets to build after installing headers for a subproject
+#    BEFORE_BUILD_RECURSION: targets to make before building subprojects
+#    BEFORE_BUILD: targets to make before a build, but after subprojects
+#    AFTER_BUILD: targets to make after a build
+#
+#    BEFORE_INSTALL: targets to build before installing the product
+#    AFTER_INSTALL: targets to build after installing the product
+#    BEFORE_POSTINSTALL: targets to build before postinstalling every subproject
+#    AFTER_POSTINSTALL: targts to build after postinstalling every subproject
+#
+#    BEFORE_INSTALLHDRS: targets to build before installing headers for a 
+#         subproject
+#    AFTER_INSTALLHDRS: targets to build after installing headers for a subproject
+#    BEFORE_INSTALLSRC: targets to build before installing source for a subproject
+#    AFTER_INSTALLSRC: targets to build after installing source for a subproject
+#
+#    BEFORE_DEPEND: targets to build before building dependencies for a
+#        subproject
+#    AFTER_DEPEND: targets to build after building dependencies for a
+#        subproject
+#
+#    AUTOMATIC_DEPENDENCY_INFO: if YES, then the dependency file is
+#        updated every time the project is built.  If NO, the dependency
+#        file is only built when the depend target is invoked.
+
+# Framework-related variables:
+#    FRAMEWORK_DLL_INSTALLDIR:  On Windows platforms, this variable indicates
+#      where to put the framework's DLL.  This variable defaults to 
+#      $(INSTALLDIR)/../Executables
+
+# Library-related variables:
+#    PUBLIC_HEADER_DIR:  Determines where public exported header files
+#      should be installed.  Do not include $(DSTROOT) in this value --
+#      it is prefixed automatically.  For library projects you should
+#       set this to something like /Developer/Headers/$(NAME).  Do not set
+#       this variable for framework projects unless you do not want the
+#       header files included in the framework.
+#    PRIVATE_HEADER_DIR:  Determines where private exported header files
+#      should be installed.  Do not include $(DSTROOT) in this value --
+#      it is prefixed automatically.
+#    LIBRARY_STYLE:  This may be either STATIC or DYNAMIC, and determines
+#      whether the libraries produced are statically linked when they
+#      are used or if they are dynamically loadable. This defaults to
+#       DYNAMIC.
+#    LIBRARY_DLL_INSTALLDIR:  On Windows platforms, this variable indicates
+#      where to put the library's DLL.  This variable defaults to 
+#      $(INSTALLDIR)/../Executables
+#
+#    INSTALL_AS_USER: owner of the intalled products (default root)
+#    INSTALL_AS_GROUP: group of the installed products (default wheel)
+#    INSTALL_PERMISSIONS: permissions of the installed product (default o+rX)
+#
+#    OTHER_RECURSIVE_VARIABLES: The names of variables which you want to be
+#      passed on the command line to recursive invocations of make.  Note that
+#      the values in OTHER_*FLAGS are inherited by subprojects automatically --
+#      you do not have to (and shouldn't) add OTHER_*FLAGS to 
+#      OTHER_RECURSIVE_VARIABLES. 
+
+# Additional headers to export beyond those in the PB.project:
+#    OTHER_PUBLIC_HEADERS
+#    OTHER_PROJECT_HEADERS
+#    OTHER_PRIVATE_HEADERS
+
+# Additional files for the project's product: <<path relative to proj?>>
+#    OTHER_RESOURCES: (non-localized) resources for this project
+#    OTHER_OFILES: relocatables to be linked into this project
+#    OTHER_LIBS: more libraries to link against
+#    OTHER_PRODUCT_DEPENDS: other dependencies of this project
+#    OTHER_SOURCEFILES: other source files maintained by .pre/postamble
+#    OTHER_GARBAGE: additional files to be removed by `make clean'
+
+# Set this to YES if you don't want a final libtool call for a library/framework.
+#    BUILD_OFILES_LIST_ONLY
+
+# To include a version string, project source must exist in a directory named 
+# $(NAME).%d[.%d][.%d] and the following line must be uncommented.
+OTHER_GENERATED_OFILES = $(VERS_OFILE)
+
+# This definition will suppress stripping of debug symbols when an executable
+# is installed.  By default it is YES.
+# STRIP_ON_INSTALL = NO
+
+# Uncomment to suppress generation of a KeyValueCoding index when installing 
+# frameworks (This index is used by WOB and IB to determine keys available
+# for an object).  Set to YES by default.
+# PREINDEX_FRAMEWORK = NO
+
+# Change this definition to install projects somewhere other than the
+# standard locations.  NEXT_ROOT defaults to "C:/Apple" on Windows systems
+# and "" on other systems.
+DSTROOT = $(HOME)
diff --git a/scselect.tproj/PB.project b/scselect.tproj/PB.project
new file mode 100644 (file)
index 0000000..5e4c7da
--- /dev/null
@@ -0,0 +1,30 @@
+{
+    DYNAMIC_CODE_GEN = YES; 
+    FILESTABLE = {
+        FRAMEWORKS = (SystemConfiguration.framework); 
+        H_FILES = (); 
+        OTHER_LINKED = (scselect.c); 
+        OTHER_SOURCES = (Makefile.preamble, Makefile, Makefile.postamble, m.template, h.template); 
+        PRECOMPILED_HEADERS = (); 
+        PROJECT_HEADERS = (); 
+        PUBLIC_HEADERS = (); 
+        SUBPROJECTS = (); 
+    }; 
+    LANGUAGE = English; 
+    MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; 
+    NEXTSTEP_BUILDTOOL = /usr/bin/gnumake; 
+    NEXTSTEP_INSTALLDIR = /usr/sbin; 
+    NEXTSTEP_JAVA_COMPILER = /usr/bin/javac; 
+    NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc; 
+    PDO_UNIX_BUILDTOOL = $NEXT_ROOT/Developer/bin/make; 
+    PDO_UNIX_INSTALLDIR = /bin; 
+    PDO_UNIX_JAVA_COMPILER = "$(JDKBINDIR)/javac"; 
+    PDO_UNIX_OBJCPLUS_COMPILER = "$(NEXTDEV_BIN)/gcc"; 
+    PROJECTNAME = scselect; 
+    PROJECTTYPE = Tool; 
+    PROJECTVERSION = 2.8; 
+    WINDOWS_BUILDTOOL = $NEXT_ROOT/Developer/Executables/make; 
+    WINDOWS_INSTALLDIR = /Library/Executables; 
+    WINDOWS_JAVA_COMPILER = "$(JDKBINDIR)/javac.exe"; 
+    WINDOWS_OBJCPLUS_COMPILER = "$(DEVDIR)/gcc"; 
+}
diff --git a/scselect.tproj/h.template b/scselect.tproj/h.template
new file mode 100644 (file)
index 0000000..f3c1b04
--- /dev/null
@@ -0,0 +1,11 @@
+$$
+/* $FILENAME$ created by $USERNAME$ on $DATE$ */
+
+#import <Foundation/Foundation.h>
+
+@interface $FILENAMESANSEXTENSION$ : NSObject
+{
+
+}
+
+@end
diff --git a/scselect.tproj/m.template b/scselect.tproj/m.template
new file mode 100644 (file)
index 0000000..1216fe5
--- /dev/null
@@ -0,0 +1,18 @@
+$$ Lines starting with $$ are not inserted into newly created files
+$$ The following substitutions are made:
+$$
+$$ $FILENAME$                e.g. foo.m
+$$ $FILENAMESANSEXTENSION$   e.g. foo
+$$ $DIRECTORY$               e.g. /tmp/MyNewApp
+$$ $PROJECTNAME$             e.g. MyNewApp
+$$ $SUBPROJECTNAME$          e.g. TheGoodPart.subproj
+$$ $USERNAME$                e.g. mwagner
+$$ $DATE$                    e.g. Jan-1-1994
+$$
+/* $FILENAME$ created by $USERNAME$ on $DATE$ */
+
+#import "$FILENAMESANSEXTENSION$.h"
+
+@implementation $FILENAMESANSEXTENSION$
+
+@end
diff --git a/scselect.tproj/scselect.c b/scselect.tproj/scselect.c
new file mode 100644 (file)
index 0000000..b889b4f
--- /dev/null
@@ -0,0 +1,258 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include <SystemConfiguration/SystemConfiguration.h>
+#include <unistd.h>
+
+
+boolean_t      apply   = TRUE;
+
+
+void
+usage(const char *command)
+{
+       SCDLog(LOG_ERR, CFSTR("usage: %s [-n] new-set-name"), command);
+       return;
+}
+
+
+int
+main(int argc, char **argv)
+{
+       const char              *command        = argv[0];
+       extern int              optind;
+       int                     opt;
+       CFStringRef             current         = NULL;
+       int                     currentMatched  = 0;
+       CFStringRef             newSet          = NULL; /* set key */
+       CFStringRef             newSetUDN       = NULL; /* user defined name */
+       CFStringRef             prefix;
+       SCPStatus               status;
+       SCPSessionRef           session;
+       CFDictionaryRef         sets;
+       CFIndex                 nSets;
+       void                    **setKeys;
+       void                    **setVals;
+       CFIndex                 i;
+
+       /* process any arguments */
+
+       SCDOptionSet(NULL, kSCDOptionUseSyslog, FALSE);
+
+       while ((opt = getopt(argc, argv, "dvn")) != -1)
+               switch(opt) {
+               case 'd':
+                       SCDOptionSet(NULL, kSCDOptionDebug, TRUE);
+                       break;
+               case 'v':
+                       SCDOptionSet(NULL, kSCDOptionVerbose, TRUE);
+                       break;
+               case 'n':
+                       apply = FALSE;
+                       break;
+               case '?':
+               default :
+                       usage(command);
+       }
+       argc -= optind;
+       argv += optind;
+
+       prefix = CFStringCreateWithFormat(NULL, NULL, CFSTR("/%@/"), kSCPrefSets);
+
+       newSet = (argc == 1)
+                       ? CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingMacRoman)
+                       : CFSTR("");
+
+       status = SCPOpen(&session, CFSTR("Select Set Command"), NULL, 0);
+       if (status != SCP_OK) {
+               SCDLog(LOG_ERR,
+                      CFSTR("SCPOpen() failed: %s"),
+                      SCPError(status));
+               exit (1);
+       }
+
+       /* check if a full path to the new "set" was specified */
+       if ((CFStringGetLength(newSet) > 0) && CFStringHasPrefix(newSet, prefix)) {
+               CFRange                 range;
+               CFMutableStringRef      str;
+
+               str = CFStringCreateMutableCopy(NULL, 0, newSet);
+               CFStringDelete(str, CFRangeMake(0, CFStringGetLength(prefix)));
+
+               range = CFStringFind(str, CFSTR("/"), 0);
+               if (range.location != kCFNotFound) {
+                       SCDLog(LOG_ERR, CFSTR("Set \"%@\" not available."), newSet);
+                       exit (1);
+               }
+
+               CFRelease(newSet);
+               newSet = str;
+       }
+
+       status = SCPGet(session, kSCPrefSets, (CFPropertyListRef *)&sets);
+       if (status != SCP_OK) {
+               SCDLog(LOG_ERR, CFSTR("SCDGet(...,%s,...) failed: %s"), SCPError(status));
+               exit (1);
+       }
+
+       status = SCPGet(session, kSCPrefCurrentSet, (CFPropertyListRef *)&current);
+       switch (status) {
+               case SCP_OK :
+                       if (CFStringHasPrefix(current, prefix)) {
+                               CFMutableStringRef      tmp;
+
+                               tmp = CFStringCreateMutableCopy(NULL, 0, current);
+                               CFStringDelete(tmp, CFRangeMake(0, CFStringGetLength(prefix)));
+                               current = tmp;
+                       } else {
+                               currentMatched = -1;    /* not prefixed */
+                       }
+                       break;
+               case SCP_NOKEY :
+                       current = CFSTR("");
+                       currentMatched = -2;    /* not defined */
+                       break;
+               default :
+                       SCDLog(LOG_ERR, CFSTR("SCDGet(...,%s,...) failed: %s"), SCPError(status));
+                       exit (1);
+       }
+
+       nSets = CFDictionaryGetCount(sets);
+       setKeys = CFAllocatorAllocate(NULL, nSets * sizeof(CFStringRef), 0);
+       setVals = CFAllocatorAllocate(NULL, nSets * sizeof(CFDictionaryRef), 0);
+       CFDictionaryGetKeysAndValues(sets, setKeys, setVals);
+
+       /* check for set with matching name */
+       for (i=0; i<nSets; i++) {
+               CFStringRef     key  = (CFStringRef)    setKeys[i];
+               CFDictionaryRef dict = (CFDictionaryRef)setVals[i];
+
+               if ((currentMatched >= 0) && CFEqual(key, current)) {
+                       currentMatched++;
+               }
+
+               if (CFEqual(newSet, key)) {
+                       newSetUDN = CFDictionaryGetValue(dict, kSCPropUserDefinedName);
+                       if (newSetUDN)  CFRetain(newSetUDN);
+                       current = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@%@"), prefix, newSet);
+                       goto found;
+               }
+       }
+
+       /* check for set with matching user-defined name */
+       for (i=0; i<nSets; i++) {
+               CFStringRef     key  = (CFStringRef)    setKeys[i];
+               CFDictionaryRef dict = (CFDictionaryRef)setVals[i];
+
+               newSetUDN = CFDictionaryGetValue(dict, kSCPropUserDefinedName);
+               if ((newSetUDN != NULL) && CFEqual(newSet, newSetUDN)) {
+                       CFRelease(newSet);
+                       newSet = CFRetain(key);
+                       CFRetain(newSetUDN);
+                       current = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@%@"), prefix, newSet);
+                       goto found;
+               }
+       }
+
+       if (argc == 2) {
+               SCDLog(LOG_ERR, CFSTR("Set \"%@\" not available."), newSet);
+       } else {
+               usage(command);
+       }
+
+       SCDLog(LOG_ERR, CFSTR(""));
+       SCDLog(LOG_ERR,
+              CFSTR("Defined sets include:%s"),
+              (currentMatched > 0) ? " (* == current set)" : "");
+
+       for (i=0; i<nSets; i++) {
+               CFStringRef     key  = (CFStringRef)    setKeys[i];
+               CFDictionaryRef dict = (CFDictionaryRef)setVals[i];
+               CFStringRef     udn  = CFDictionaryGetValue(dict, kSCPropUserDefinedName);
+
+               SCDLog(LOG_ERR,
+                       CFSTR(" %s %@\t(%@)"),
+                       ((currentMatched > 0) && CFEqual(key, current)) ? "*" : " ",
+                       key,
+                       udn ? udn : CFSTR(""));
+       }
+
+       switch (currentMatched) {
+               case -2 :
+                       SCDLog(LOG_ERR, CFSTR(""));
+                       SCDLog(LOG_ERR, CFSTR("CurrentSet not defined"));
+                       break;
+               case -1 :
+                       SCDLog(LOG_ERR, CFSTR(""));
+                       SCDLog(LOG_ERR, CFSTR("CurrentSet \"%@\" may not be valid"), current);
+                       break;
+               case  0 :
+                       SCDLog(LOG_ERR, CFSTR(""));
+                       SCDLog(LOG_ERR, CFSTR("CurrentSet \"%@\" not valid"), current);
+                       break;
+               default :
+                       break;
+       }
+
+       exit (1);
+
+    found :
+
+       status = SCPSet(session, kSCPrefCurrentSet, current);
+       if (status != SCP_OK) {
+               SCDLog(LOG_ERR,
+                       CFSTR("SCDSet(...,%@,%@) failed: %s"),
+                       kSCPrefCurrentSet,
+                       current,
+                       SCPError(status));
+               exit (1);
+       }
+
+       status = SCPCommit(session);
+       if (status != SCP_OK) {
+               SCDLog(LOG_ERR, CFSTR("SCPCommit() failed: %s"), SCPError(status));
+               exit (1);
+       }
+
+       if (apply) {
+               status = SCPApply(session);
+               if (status != SCP_OK) {
+                       SCDLog(LOG_ERR, CFSTR("SCPApply() failed: %s"), SCPError(status));
+                       exit (1);
+               }
+       }
+
+       status = SCPClose(&session);
+       if (status != SCP_OK) {
+               SCDLog(LOG_ERR, CFSTR("SCPClose() failed: %s"), SCPError(status));
+               exit (1);
+       }
+
+       SCDLog(LOG_NOTICE,
+               CFSTR("%@ updated to %@ (%@)"),
+               kSCPrefCurrentSet,
+               newSet,
+               newSetUDN ? newSetUDN : CFSTR(""));
+
+       exit (0);
+       return 0;
+}
diff --git a/scutil.tproj/Makefile b/scutil.tproj/Makefile
new file mode 100644 (file)
index 0000000..c23c5f1
--- /dev/null
@@ -0,0 +1,54 @@
+#
+# Generated by the Apple Project Builder.
+#
+# NOTE: Do NOT change this file -- Project Builder maintains it.
+#
+# Put all of your customizations in files called Makefile.preamble
+# and Makefile.postamble (both optional), and Makefile will include them.
+#
+
+NAME = scutil
+
+PROJECTVERSION = 2.8
+PROJECT_TYPE = Tool
+
+HFILES = scutil.h commands.h dictionary.h session.h cache.h notify.h\
+        tests.h
+
+CFILES = scutil.c commands.c dictionary.c session.c cache.c notify.c\
+        tests.c
+
+OTHERSRCS = Makefile.preamble Makefile Makefile.postamble m.template\
+           h.template
+
+
+MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles
+CODE_GEN_STYLE = DYNAMIC
+MAKEFILE = tool.make
+NEXTSTEP_INSTALLDIR = /usr/sbin
+WINDOWS_INSTALLDIR = /Library/Executables
+PDO_UNIX_INSTALLDIR = /bin
+LIBS =
+DEBUG_LIBS = $(LIBS)
+PROF_LIBS = $(LIBS)
+
+
+FRAMEWORKS = -framework SystemConfiguration
+
+
+NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc
+WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc
+PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc
+NEXTSTEP_JAVA_COMPILER = /usr/bin/javac
+WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe
+PDO_UNIX_JAVA_COMPILER = $(JDKBINDIR)/javac
+
+include $(MAKEFILEDIR)/platform.make
+
+-include Makefile.preamble
+
+include $(MAKEFILEDIR)/$(MAKEFILE)
+
+-include Makefile.postamble
+
+-include Makefile.dependencies
diff --git a/scutil.tproj/Makefile.postamble b/scutil.tproj/Makefile.postamble
new file mode 100644 (file)
index 0000000..b3af842
--- /dev/null
@@ -0,0 +1,100 @@
+###############################################################################
+#  Makefile.postamble
+#  Copyright 1997, Apple Computer, Inc.
+#
+#  Use this makefile, which is imported after all other makefiles, to
+#  override attributes for a project's Makefile environment. This allows you
+#  to take advantage of the environment set up by the other Makefiles.
+#  You can also define custom rules at the end of this file.
+#
+###############################################################################
+#
+# These variables are exported by the standard makefiles and can be
+# used in any customizations you make.  They are *outputs* of
+# the Makefiles and should be used, not set.
+#
+#  PRODUCTS: products to install.  All of these products will be placed in
+#       the directory $(DSTROOT)$(INSTALLDIR)
+#  GLOBAL_RESOURCE_DIR: The directory to which resources are copied.
+#  LOCAL_RESOURCE_DIR: The directory to which localized resources are copied.
+#  OFILE_DIR: Directory into which .o object files are generated.
+#  DERIVED_SRC_DIR: Directory used for all other derived files
+#
+#  ALL_CFLAGS:  flags to pass when compiling .c files
+#  ALL_MFLAGS:  flags to pass when compiling .m files
+#  ALL_CCFLAGS:  flags to pass when compiling .cc, .cxx, and .C files
+#  ALL_MMFLAGS:  flags to pass when compiling .mm, .mxx, and .M files
+#  ALL_PRECOMPFLAGS:  flags to pass when precompiling .h files
+#  ALL_LDFLAGS:  flags to pass when linking object files
+#  ALL_LIBTOOL_FLAGS:  flags to pass when libtooling object files
+#  ALL_PSWFLAGS:  flags to pass when processing .psw and .pswm (pswrap) files
+#  ALL_RPCFLAGS:  flags to pass when processing .rpc (rpcgen) files
+#  ALL_YFLAGS:  flags to pass when processing .y (yacc) files
+#  ALL_LFLAGS:  flags to pass when processing .l (lex) files
+#
+#  NAME: name of application, bundle, subproject, palette, etc.
+#  LANGUAGES: langages in which the project is written (default "English")
+#  English_RESOURCES: localized resources (e.g. nib's, images) of project
+#  GLOBAL_RESOURCES: non-localized resources of project
+#
+#  SRCROOT:  base directory in which to place the new source files
+#  SRCPATH:  relative path from SRCROOT to present subdirectory
+#
+#  INSTALLDIR: Directory the product will be installed into by 'install' target
+#  PUBLIC_HDR_INSTALLDIR: where to install public headers.  Don't forget
+#        to prefix this with DSTROOT when you use it.
+#  PRIVATE_HDR_INSTALLDIR: where to install private headers.  Don't forget
+#       to prefix this with DSTROOT when you use it.
+#
+#  EXECUTABLE_EXT: Executable extension for the platform (i.e. .exe on Windows)
+#
+###############################################################################
+
+# Some compiler flags can be overridden here for certain build situations.
+#
+#    WARNING_CFLAGS:  flag used to set warning level (defaults to -Wmost)
+#    DEBUG_SYMBOLS_CFLAGS:  debug-symbol flag passed to all builds (defaults
+#      to -g)
+#    DEBUG_BUILD_CFLAGS:  flags passed during debug builds (defaults to -DDEBUG)
+#    OPTIMIZE_BUILD_CFLAGS:  flags passed during optimized builds (defaults
+#      to -O)
+#    PROFILE_BUILD_CFLAGS:  flags passed during profile builds (defaults
+#      to -pg -DPROFILE)
+#    LOCAL_DIR_INCLUDE_DIRECTIVE:  flag used to add current directory to
+#      the include path (defaults to -I.)
+#    DEBUG_BUILD_LDFLAGS, OPTIMIZE_BUILD_LDFLAGS, PROFILE_BUILD_LDFLAGS: flags
+#      passed to ld/libtool (defaults to nothing)
+
+
+# Library and Framework projects only:
+#    INSTALL_NAME_DIRECTIVE:  This directive ensures that executables linked
+#      against the framework will run against the correct version even if
+#      the current version of the framework changes.  You may override this
+#      to "" as an alternative to using the DYLD_LIBRARY_PATH during your
+#      development cycle, but be sure to restore it before installing.
+
+
+# Ownership and permissions of files installed by 'install' target
+
+#INSTALL_AS_USER = root
+       # User/group ownership
+#INSTALL_AS_GROUP = wheel
+       # (probably want to set both of these)
+#INSTALL_PERMISSIONS =
+       # If set, 'install' chmod's executable to this
+
+
+# Options to strip.  Note: -S strips debugging symbols (executables can be stripped
+# down further with -x or, if they load no bundles, with no options at all).
+
+#STRIPFLAGS = -S
+
+
+#########################################################################
+# Put rules to extend the behavior of the standard Makefiles here.  Include them in
+# the dependency tree via cvariables like AFTER_INSTALL in the Makefile.preamble.
+#
+# You should avoid redefining things like "install" or "app", as they are
+# owned by the top-level Makefile API and no context has been set up for where
+# derived files should go.
+#
diff --git a/scutil.tproj/Makefile.preamble b/scutil.tproj/Makefile.preamble
new file mode 100644 (file)
index 0000000..b9f5238
--- /dev/null
@@ -0,0 +1,137 @@
+###############################################################################
+#  Makefile.preamble
+#  Copyright 1997, Apple Computer, Inc.
+#
+#  Use this makefile for configuring the standard application makefiles
+#  associated with ProjectBuilder. It is included before the main makefile.
+#  In Makefile.preamble you set attributes for a project, so they are available
+#  to the project's makefiles.  In contrast, you typically write additional rules or
+#  override built-in behavior in the Makefile.postamble.
+#
+#  Each directory in a project tree (main project plus subprojects) should
+#  have its own Makefile.preamble and Makefile.postamble.
+###############################################################################
+#
+# Before the main makefile is included for this project, you may set:
+#
+#    MAKEFILEDIR: Directory in which to find $(MAKEFILE)
+#    MAKEFILE: Top level mechanism Makefile (e.g., app.make, bundle.make)
+
+# Compiler/linker flags added to the defaults:  The OTHER_* variables will be
+# inherited by all nested sub-projects, but the LOCAL_ versions of the same
+# variables will not.  Put your -I, -D, -U, and -L flags in ProjectBuilder's
+# Build Attributes inspector if at all possible.  To override the default flags
+# that get passed to ${CC} (e.g. change -O to -O2), see Makefile.postamble.  The
+# variables below are *inputs* to the build process and distinct from the override
+# settings done (less often) in the Makefile.postamble.
+#
+#    OTHER_CFLAGS, LOCAL_CFLAGS:  additional flags to pass to the compiler
+#      Note that $(OTHER_CFLAGS) and $(LOCAL_CFLAGS) are used for .h, ...c, .m,
+#      .cc, .cxx, .C, and .M files.  There is no need to respecify the
+#      flags in OTHER_MFLAGS, etc.
+#    OTHER_MFLAGS, LOCAL_MFLAGS:  additional flags for .m files
+#    OTHER_CCFLAGS, LOCAL_CCFLAGS:  additional flags for .cc, .cxx, and ...C files
+#    OTHER_MMFLAGS, LOCAL_MMFLAGS:  additional flags for .mm and .M files
+#    OTHER_PRECOMPFLAGS, LOCAL_PRECOMPFLAGS:  additional flags used when
+#      precompiling header files
+#    OTHER_LDFLAGS, LOCAL_LDFLAGS:  additional flags passed to ld and libtool
+#    OTHER_PSWFLAGS, LOCAL_PSWFLAGS:  additional flags passed to pswrap
+#    OTHER_RPCFLAGS, LOCAL_RPCFLAGS:  additional flags passed to rpcgen
+#    OTHER_YFLAGS, LOCAL_YFLAGS:  additional flags passed to yacc
+#    OTHER_LFLAGS, LOCAL_LFLAGS:  additional flags passed to lex
+
+# These variables provide hooks enabling you to add behavior at almost every
+# stage of the make:
+#
+#    BEFORE_PREBUILD: targets to build before installing headers for a subproject
+#    AFTER_PREBUILD: targets to build after installing headers for a subproject
+#    BEFORE_BUILD_RECURSION: targets to make before building subprojects
+#    BEFORE_BUILD: targets to make before a build, but after subprojects
+#    AFTER_BUILD: targets to make after a build
+#
+#    BEFORE_INSTALL: targets to build before installing the product
+#    AFTER_INSTALL: targets to build after installing the product
+#    BEFORE_POSTINSTALL: targets to build before postinstalling every subproject
+#    AFTER_POSTINSTALL: targts to build after postinstalling every subproject
+#
+#    BEFORE_INSTALLHDRS: targets to build before installing headers for a
+#         subproject
+#    AFTER_INSTALLHDRS: targets to build after installing headers for a subproject
+#    BEFORE_INSTALLSRC: targets to build before installing source for a subproject
+#    AFTER_INSTALLSRC: targets to build after installing source for a subproject
+#
+#    BEFORE_DEPEND: targets to build before building dependencies for a
+#        subproject
+#    AFTER_DEPEND: targets to build after building dependencies for a
+#        subproject
+#
+#    AUTOMATIC_DEPENDENCY_INFO: if YES, then the dependency file is
+#        updated every time the project is built.  If NO, the dependency
+#        file is only built when the depend target is invoked.
+
+# Framework-related variables:
+#    FRAMEWORK_DLL_INSTALLDIR:  On Windows platforms, this variable indicates
+#      where to put the framework's DLL.  This variable defaults to
+#      $(INSTALLDIR)/../Executables
+
+# Library-related variables:
+#    PUBLIC_HEADER_DIR:  Determines where public exported header files
+#      should be installed.  Do not include $(DSTROOT) in this value --
+#      it is prefixed automatically.  For library projects you should
+#       set this to something like /Developer/Headers/$(NAME).  Do not set
+#       this variable for framework projects unless you do not want the
+#       header files included in the framework.
+#    PRIVATE_HEADER_DIR:  Determines where private exported header files
+#      should be installed.  Do not include $(DSTROOT) in this value --
+#      it is prefixed automatically.
+#    LIBRARY_STYLE:  This may be either STATIC or DYNAMIC, and determines
+#      whether the libraries produced are statically linked when they
+#      are used or if they are dynamically loadable. This defaults to
+#       DYNAMIC.
+#    LIBRARY_DLL_INSTALLDIR:  On Windows platforms, this variable indicates
+#      where to put the library's DLL.  This variable defaults to
+#      $(INSTALLDIR)/../Executables
+#
+#    INSTALL_AS_USER: owner of the intalled products (default root)
+#    INSTALL_AS_GROUP: group of the installed products (default wheel)
+#    INSTALL_PERMISSIONS: permissions of the installed product (default o+rX)
+#
+#    OTHER_RECURSIVE_VARIABLES: The names of variables which you want to be
+#      passed on the command line to recursive invocations of make.  Note that
+#      the values in OTHER_*FLAGS are inherited by subprojects automatically --
+#      you do not have to (and shouldn't) add OTHER_*FLAGS to
+#      OTHER_RECURSIVE_VARIABLES.
+
+# Additional headers to export beyond those in the PB.project:
+#    OTHER_PUBLIC_HEADERS
+#    OTHER_PROJECT_HEADERS
+#    OTHER_PRIVATE_HEADERS
+
+# Additional files for the project's product: <<path relative to proj?>>
+#    OTHER_RESOURCES: (non-localized) resources for this project
+#    OTHER_OFILES: relocatables to be linked into this project
+#    OTHER_LIBS: more libraries to link against
+#    OTHER_PRODUCT_DEPENDS: other dependencies of this project
+#    OTHER_SOURCEFILES: other source files maintained by .pre/postamble
+#    OTHER_GARBAGE: additional files to be removed by `make clean'
+
+# Set this to YES if you don't want a final libtool call for a library/framework.
+#    BUILD_OFILES_LIST_ONLY
+
+# To include a version string, project source must exist in a directory named
+# $(NAME).%d[.%d][.%d] and the following line must be uncommented.
+OTHER_GENERATED_OFILES = $(VERS_OFILE)
+
+# This definition will suppress stripping of debug symbols when an executable
+# is installed.  By default it is YES.
+# STRIP_ON_INSTALL = NO
+
+# Uncomment to suppress generation of a KeyValueCoding index when installing
+# frameworks (This index is used by WOB and IB to determine keys available
+# for an object).  Set to YES by default.
+# PREINDEX_FRAMEWORK = NO
+
+# Change this definition to install projects somewhere other than the
+# standard locations.  NEXT_ROOT defaults to "C:/Apple" on Windows systems
+# and "" on other systems.
+# DSTROOT = $(HOME)
diff --git a/scutil.tproj/PB.project b/scutil.tproj/PB.project
new file mode 100644 (file)
index 0000000..c1ceca4
--- /dev/null
@@ -0,0 +1,28 @@
+{
+    DYNAMIC_CODE_GEN = YES;
+    FILESTABLE = {
+       FRAMEWORKS = (SystemConfiguration.framework);
+       FRAMEWORKSEARCH = ();
+       H_FILES = (scutil.h, commands.h, dictionary.h, session.h, cache.h, notify.h, tests.h);
+       OTHER_LINKED = (scutil.c, commands.c, dictionary.c, session.c, cache.c, notify.c, tests.c);
+       OTHER_SOURCES = (Makefile.preamble, Makefile, Makefile.postamble, m.template, h.template);
+       SUBPROJECTS = ();
+    };
+    LANGUAGE = English;
+    MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles";
+    NEXTSTEP_BUILDTOOL = /usr/bin/gnumake;
+    NEXTSTEP_INSTALLDIR = /usr/sbin;
+    NEXTSTEP_JAVA_COMPILER = /usr/bin/javac;
+    NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc;
+    PDO_UNIX_BUILDTOOL = $NEXT_ROOT/Developer/bin/make;
+    PDO_UNIX_INSTALLDIR = /bin;
+    PDO_UNIX_JAVA_COMPILER = "$(JDKBINDIR)/javac";
+    PDO_UNIX_OBJCPLUS_COMPILER = "$(NEXTDEV_BIN)/gcc";
+    PROJECTNAME = scutil;
+    PROJECTTYPE = Tool;
+    PROJECTVERSION = 2.8;
+    WINDOWS_BUILDTOOL = $NEXT_ROOT/Developer/Executables/make;
+    WINDOWS_INSTALLDIR = /Library/Executables;
+    WINDOWS_JAVA_COMPILER = "$(JDKBINDIR)/javac.exe";
+    WINDOWS_OBJCPLUS_COMPILER = "$(DEVDIR)/gcc";
+}
diff --git a/scutil.tproj/cache.c b/scutil.tproj/cache.c
new file mode 100644 (file)
index 0000000..759959b
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include <sys/types.h>
+
+#include "scutil.h"
+
+void
+do_list(int argc, char **argv)
+{
+       CFStringRef             key;
+       int                     regexOptions = 0;
+       SCDStatus               status;
+       CFArrayRef              list;
+       CFIndex                 listCnt;
+       int                     i;
+
+       key = CFStringCreateWithCString(NULL,
+                                       (argc >= 1) ? argv[0] : "",
+                                       kCFStringEncodingMacRoman);
+
+       if (argc == 2)
+               regexOptions = kSCDRegexKey;
+
+       status = SCDList(session, key, regexOptions, &list);
+       CFRelease(key);
+       if (status != SCD_OK) {
+               SCDLog(LOG_INFO, CFSTR("SCDList: %s"), SCDError(status));
+               return;
+       }
+
+       listCnt = CFArrayGetCount(list);
+       if (listCnt > 0) {
+               for (i=0; i<listCnt; i++) {
+                       SCDLog(LOG_NOTICE, CFSTR("  subKey [%d] = %@"), i, CFArrayGetValueAtIndex(list, i));
+               }
+       } else {
+               SCDLog(LOG_NOTICE, CFSTR("  no subKey's"));
+       }
+       CFRelease(list);
+
+       return;
+}
+
+
+void
+do_add(int argc, char **argv)
+{
+       CFStringRef     key;
+       SCDStatus       status;
+
+       key    = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingMacRoman);
+
+       if (argc < 2) {
+               status = SCDAdd(session, key, data);
+               if (status != SCD_OK) {
+                       SCDLog(LOG_INFO, CFSTR("SCDAdd: %s"), SCDError(status));
+               }
+       } else {
+               status = SCDAddSession(session, key, data);
+               if (status != SCD_OK) {
+                       SCDLog(LOG_INFO, CFSTR("SCDAddSession: %s"), SCDError(status));
+               }
+       }
+
+       CFRelease(key);
+       return;
+}
+
+
+void
+do_get(int argc, char **argv)
+{
+       SCDStatus       status;
+       CFStringRef     key;
+       SCDHandleRef    newData = NULL;
+
+       key    = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingMacRoman);
+       status = SCDGet(session, key, &newData);
+       CFRelease(key);
+       if (status != SCD_OK) {
+               SCDLog(LOG_INFO, CFSTR("SCDGet: %s"), SCDError(status));
+               if (newData != NULL) {
+                       SCDHandleRelease(newData);      /* toss the handle from SCDGet() */
+               }
+               return;
+       }
+
+       if (data != NULL) {
+               SCDHandleRelease(data);         /* we got a new handle from SCDGet() */
+       }
+       data = newData;
+
+       return;
+}
+
+
+void
+do_set(int argc, char **argv)
+{
+       SCDStatus       status;
+       CFStringRef     key;
+
+       key    = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingMacRoman);
+       status = SCDSet(session, key, data);
+       CFRelease(key);
+       if (status != SCD_OK) {
+               SCDLog(LOG_INFO, CFSTR("SCDSet: %s"), SCDError(status));
+       }
+       return;
+}
+
+
+void
+do_remove(int argc, char **argv)
+{
+       SCDStatus       status;
+       CFStringRef     key;
+
+       key    = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingMacRoman);
+       status = SCDRemove(session, key);
+       CFRelease(key);
+       if (status != SCD_OK) {
+               SCDLog(LOG_INFO, CFSTR("SCDRemove: %s"), SCDError(status));
+       }
+       return;
+}
+
+
+void
+do_touch(int argc, char **argv)
+{
+       SCDStatus       status;
+       CFStringRef     key;
+
+       key    = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingMacRoman);
+       status = SCDTouch(session, key);
+       CFRelease(key);
+       if (status != SCD_OK) {
+               SCDLog(LOG_INFO, CFSTR("SCDTouch: %s"), SCDError(status));
+       }
+       return;
+}
diff --git a/scutil.tproj/cache.h b/scutil.tproj/cache.h
new file mode 100644 (file)
index 0000000..3b02426
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#ifndef _CACHE_H
+#define _CACHE_H
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+void   do_list                 __P((int argc, char **argv));
+void   do_add                  __P((int argc, char **argv));
+void   do_get                  __P((int argc, char **argv));
+void   do_set                  __P((int argc, char **argv));
+void   do_remove               __P((int argc, char **argv));
+void   do_touch                __P((int argc, char **argv));
+
+__END_DECLS
+
+#endif /* !_CACHE_H */
diff --git a/scutil.tproj/commands.c b/scutil.tproj/commands.c
new file mode 100644 (file)
index 0000000..501b23f
--- /dev/null
@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/errno.h>
+
+#include "scutil.h"
+#include "commands.h"
+#include "dictionary.h"
+#include "session.h"
+#include "cache.h"
+#include "notify.h"
+#include "tests.h"
+
+#include "SCDPrivate.h"
+
+const cmdInfo commands[] = {
+       /* cmd          minArgs maxArgs func                                    */
+       /*      usage                                                           */
+
+       { "help",       0,      0,      do_help,                0,
+               " help                          : list available commands"                      },
+
+       { "f.read",     1,      1,      do_readFile,            0,
+               " f.read file                   : process commands from file"                   },
+
+       /* local dictionary manipulation commands */
+
+       { "d.init",     0,      0,      do_dictInit,            1,
+               " d.init                        : initialize (empty) dictionary"                },
+
+       { "d.show",     0,      0,      do_dictShow,            1,
+               " d.show                        : show dictionary contents"                     },
+
+       { "d.add",      2,      101,    do_dictSetKey,          1,
+               " d.add key [*#?] val [v2 ...]  : add information to dictionary\n"
+               "       (*=array, #=number, ?=boolean)"                         },
+
+       { "d.remove",   1,      1,      do_dictRemoveKey,               1,
+               " d.remove key                  : remove key from dictionary"                   },
+
+       /* data store manipulation commands */
+
+       { "open",       0,      0,      do_open,                2,
+               " open                          : open a session with \"configd\""              },
+
+       { "close",      0,      0,      do_close,               2,
+               " close                         : close current \"configd\" session"            },
+
+       { "lock",       0,      0,      do_lock,                3,
+               " lock                          : secures write access to data store"           },
+
+       { "unlock",     0,      0,      do_unlock,              3,
+               " unlock                        : secures write access to data store"           },
+
+       { "list",       0,      2,      do_list,                4,
+               " list [prefix] [regex]         : list keys in data store"                      },
+
+       { "add",        1,      2,      do_add,                 4,
+               " add key [session]             : add key in data store w/current dict"         },
+
+       { "get",        1,      1,      do_get,                 4,
+               " get key                       : get dict from data store w/key"               },
+
+       { "set",        1,      1,      do_set,                 4,
+               " set key                       : set key in data store w/current dict"         },
+
+       { "remove",     1,      1,      do_remove,              4,
+               " remove key                    : remove key from data store"                   },
+
+       { "touch",      1,      1,      do_touch,               4,
+               " touch key                     : touch key in data store"                      },
+
+       { "n.list",     0,      1,      do_notify_list,         5,
+               " n.list [regex]                : list notification keys"                       },
+
+       { "n.add",      1,      2,      do_notify_add,          5,
+               " n.add key [regex]             : add notification key"                         },
+
+       { "n.remove",   1,      2,      do_notify_remove,       5,
+               " n.remove key [regex]          : remove notification key"                      },
+
+       { "n.changes",  0,      0,      do_notify_changes,      5,
+               " n.changes                     : list changed keys"                            },
+
+       { "n.wait",     0,      0,      do_notify_wait,         5,
+               " n.wait                        : wait for changes"                             },
+
+       { "n.watch",    0,      1,      do_notify_callback,     5,
+               " n.watch [verbose]             : watch for changes"                            },
+
+       { "n.signal",   1,      2,      do_notify_signal,       5,
+               " n.signal sig [pid]            : signal changes"                               },
+
+       { "n.file",     0,      1,      do_notify_file,         5,
+               " n.file [identifier]           : watch for changes via file"                   },
+
+       { "n.cancel",   0,      1,      do_notify_cancel,       5,
+               " n.cancel                      : cancel notification requests"                 },
+
+       { "snapshot",   0,      0,      do_snapshot,            9,
+               " snapshot                      : save snapshot of cache and session data"      },
+
+#ifdef DEBUG
+       { "t.ocleak",   0,      1,      test_openCloseLeak,     9,
+               " t.ocleak [#]                  : test for leaks (open/close)"                  },
+#endif /* DEBUG */
+};
+
+const int nCommands = (sizeof(commands)/sizeof(cmdInfo));
+
+
+void
+do_command(int argc, char **argv)
+{
+       int     i;
+       char    *cmd = argv[0];
+
+       for (i=0; i<nCommands; i++) {
+               if (strcasecmp(cmd, commands[i].cmd) == 0) {
+                       --argc;
+                       argv++;
+                       if (argc < commands[i].minArgs) {
+                               SCDLog(LOG_INFO, CFSTR("%s: too few arguments"), cmd);
+                               return;
+                       } else if (argc > commands[i].maxArgs) {
+                               SCDLog(LOG_INFO, CFSTR("%s: too many arguments"), cmd);
+                               return;
+                       }
+                       commands[i].func(argc, argv);
+                       return;
+               }
+       }
+
+       SCDLog(LOG_INFO, CFSTR("%s: unknown, type \"help\" for command info"), cmd);
+       return;
+}
+
+
+void
+do_help(int argc, char **argv)
+{
+       int     g = -1;         /* current group */
+       int     i;
+
+       SCDLog(LOG_NOTICE, CFSTR(""));
+       SCDLog(LOG_NOTICE, CFSTR("Available commands:"));
+       for (i=0; i<nCommands; i++) {
+               if (g != commands[i].group) {
+                       SCDLog(LOG_NOTICE, CFSTR(""));
+                       g = commands[i].group;
+               }
+               SCDLog(LOG_NOTICE, CFSTR("%s"), commands[i].usage);
+       }
+       SCDLog(LOG_NOTICE, CFSTR(""));
+
+       return;
+}
+
+
+void
+do_readFile(int argc, char **argv)
+{
+       FILE            *fp = fopen(argv[0], "r");
+       boolean_t       ok;
+
+       if (fp == NULL) {
+               SCDLog(LOG_INFO, CFSTR("f.read: could not open file (%s)."), strerror(errno));
+               return;
+       }
+
+       /* open file, increase nesting level */
+       SCDLog(LOG_DEBUG, CFSTR("f.read: reading file (%s)."), argv[0]);
+       nesting++;
+
+       if (SCDOptionGet(NULL, kSCDOptionUseCFRunLoop)) {
+               CFSocketRef             in;
+               CFSocketContext         context = { 0, fp, NULL, NULL, NULL };
+               CFRunLoopSourceRef      rls;
+
+               /* create a "socket" reference with the file descriptor associated with stdin */
+               in  = CFSocketCreateWithNative(NULL,
+                                              fileno(fp),
+                                              kCFSocketReadCallBack,
+                                              runLoopProcessInput,
+                                              &context);
+
+               /* Create and add a run loop source for the file descriptor */
+               rls = CFSocketCreateRunLoopSource(NULL, in, nesting);
+
+               /*
+                * Remove the current input file from the run loop sources. We
+                * will reactivate the current input file source when we are
+                * finished reading data from the new file.
+                */
+               CFRunLoopRemoveSource(CFRunLoopGetCurrent(),
+                                     (CFRunLoopSourceRef) CFArrayGetValueAtIndex(sources, 0),
+                                     kCFRunLoopDefaultMode);
+
+               /* keep track of this new source */
+               CFArrayInsertValueAtIndex(sources, 0, rls);
+
+               /* add this source to the run loop */
+               CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
+
+               CFRelease(rls);
+               CFRelease(in);
+       } else {
+               do {
+                       /* debug information, diagnostics */
+                       _showMachPortStatus();
+
+                       /* process command */
+                       ok = process_line(fp);
+               } while (ok);
+
+               /* decrement the nesting level */
+               nesting--;
+       }
+
+       return;
+}
diff --git a/scutil.tproj/commands.h b/scutil.tproj/commands.h
new file mode 100644 (file)
index 0000000..c36a766
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#ifndef _COMMANDS_H
+#define _COMMANDS_H
+
+#include <sys/cdefs.h>
+
+typedef struct {
+       char    *cmd;
+       int     minArgs;
+       int     maxArgs;
+       void    (*func)();
+       int     group;
+       char    *usage;
+} cmdInfo;
+
+extern const cmdInfo   commands[];
+extern const int       nCommands;
+
+__BEGIN_DECLS
+
+void   do_command              __P((int argc, char **argv));
+void   do_help                 __P((int argc, char **argv));
+void   do_readFile             __P((int argc, char **argv));
+
+__END_DECLS
+
+#endif /* !_COMMANDS_H */
diff --git a/scutil.tproj/dictionary.c b/scutil.tproj/dictionary.c
new file mode 100644 (file)
index 0000000..35371d4
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include "scutil.h"
+
+
+//#include <stdlib.h>
+//#include <limits.h>
+
+
+void
+do_dictInit(int argc, char **argv)
+{
+       CFMutableDictionaryRef  dict;
+
+       if (data != NULL) {
+               SCDHandleRelease(data);
+       }
+
+       data = SCDHandleInit();
+       dict = CFDictionaryCreateMutable(NULL
+                                        ,0
+                                        ,&kCFTypeDictionaryKeyCallBacks
+                                        ,&kCFTypeDictionaryValueCallBacks
+                                        );
+       SCDHandleSetData(data, dict);
+       CFRelease(dict);
+
+       return;
+}
+
+
+void
+do_dictShow(int argc, char **argv)
+{
+       int                     instance;
+       CFPropertyListRef       store;
+
+       if (data == NULL) {
+               SCDLog(LOG_INFO, CFSTR("d.show: dictionary must be initialized."));
+               return;
+       }
+
+       instance = SCDHandleGetInstance(data);
+       store    = SCDHandleGetData(data);
+
+       SCDLog(LOG_NOTICE, CFSTR("dict (instance = %d) = \n\t%@"), instance, store);
+
+       return;
+}
+
+
+void
+do_dictSetKey(int argc, char **argv)
+{
+       CFPropertyListRef       store;
+       CFStringRef             key;
+       CFPropertyListRef       value     = NULL;
+       CFMutableArrayRef       array     = NULL;
+       boolean_t               doArray   = FALSE;
+       boolean_t               doBoolean = FALSE;
+       boolean_t               doNumeric = FALSE;
+
+       if (data == NULL) {
+               SCDLog(LOG_INFO, CFSTR("d.add: dictionary must be initialized."));
+               return;
+       }
+
+       store = SCDHandleGetData(data);
+       if (CFGetTypeID(store) != CFDictionaryGetTypeID()) {
+               SCDLog(LOG_INFO, CFSTR("d.add: data (fetched from configuration server) is not a dictionary"));
+               return;
+       }
+
+
+       key = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingMacRoman);
+       argv++; argc--;
+
+       while (argc > 0) {
+               if (strcmp(argv[0], "*") == 0) {
+                       /* if array requested */
+                       doArray = TRUE;
+               } else if (strcmp(argv[0], "-") == 0) {
+                       /* if string values requested */
+               } else if (strcmp(argv[0], "?") == 0) {
+                       /* if boolean values requested */
+                       doBoolean = TRUE;
+               } else if (strcmp(argv[0], "#") == 0) {
+                       /* if numeric values requested */
+                       doNumeric = TRUE;
+               } else {
+                       /* it's not a special flag */
+                       break;
+               }
+               argv++; argc--;
+       }
+
+       if (argc > 1) {
+               doArray = TRUE;
+       } else if (!doArray && (argc == 0)) {
+               SCDLog(LOG_INFO, CFSTR("d.add: no values"));
+               return;
+       }
+
+       if (doArray) {
+               array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+       }
+
+       while (argc > 0) {
+               if (doBoolean) {
+                       if         ((strcasecmp(argv[0], "true") == 0) ||
+                                   (strcasecmp(argv[0], "t"   ) == 0) ||
+                                   (strcasecmp(argv[0], "yes" ) == 0) ||
+                                   (strcasecmp(argv[0], "y"   ) == 0) ||
+                                   (strcmp    (argv[0], "1"   ) == 0)) {
+                               value = CFRetain(kCFBooleanTrue);
+                       } else if ((strcasecmp(argv[0], "false") == 0) ||
+                                  (strcasecmp(argv[0], "f"    ) == 0) ||
+                                  (strcasecmp(argv[0], "no"   ) == 0) ||
+                                  (strcasecmp(argv[0], "n"    ) == 0) ||
+                                  (strcmp    (argv[0], "0"    ) == 0)) {
+                               value = CFRetain(kCFBooleanFalse);
+                       } else {
+                               SCDLog(LOG_INFO, CFSTR("d.add: invalid data"));
+                               if (doArray) {
+                                       CFRelease(array);
+                               }
+                               return;
+                       }
+               } else if (doNumeric) {
+                       int     intValue;
+
+                       if (sscanf(argv[0], "%d", &intValue) == 1) {
+                               value = CFNumberCreate(NULL, kCFNumberIntType, &intValue);
+                       } else {
+                               SCDLog(LOG_INFO, CFSTR("d.add: invalid data"));
+                               if (doArray) {
+                                       CFRelease(array);
+                               }
+                               return;
+                       }
+               } else {
+                       value = (CFPropertyListRef)CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingMacRoman);
+               }
+
+               if (doArray) {
+                       CFArrayAppendValue(array, value);
+               }
+
+               argv++; argc--;
+       }
+
+       if (doArray) {
+               value = array;
+       }
+
+       CFDictionarySetValue((CFMutableDictionaryRef)store, key, value);
+       CFRelease(value);
+       CFRelease(key);
+
+       return;
+}
+
+
+void
+do_dictRemoveKey(int argc, char **argv)
+{
+       CFPropertyListRef       store;
+       CFStringRef             key;
+
+       if (data == NULL) {
+               SCDLog(LOG_INFO, CFSTR("d.remove: dictionary must be initialized."));
+               return;
+       }
+
+       store = SCDHandleGetData(data);
+       if (CFGetTypeID(store) == CFDictionaryGetTypeID()) {
+               key = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingMacRoman);
+               CFDictionaryRemoveValue((CFMutableDictionaryRef)store, key);
+               CFRelease(key);
+       } else {
+               SCDLog(LOG_INFO, CFSTR("d.add: data (fetched from configuration server) is not a dictionary"));
+       }
+
+       return;
+}
diff --git a/scutil.tproj/dictionary.h b/scutil.tproj/dictionary.h
new file mode 100644 (file)
index 0000000..8ea9cfe
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#ifndef _DICTIONARY_H
+#define _DICTIONARY_H
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+void   do_dictInit             __P((int argc, char **argv));
+void   do_dictShow             __P((int argc, char **argv));
+void   do_dictSetKey           __P((int argc, char **argv));
+void   do_dictRemoveKey                __P((int argc, char **argv));
+
+__END_DECLS
+
+#endif /* !_DICTIONARY_H */
diff --git a/scutil.tproj/h.template b/scutil.tproj/h.template
new file mode 100644 (file)
index 0000000..f3c1b04
--- /dev/null
@@ -0,0 +1,11 @@
+$$
+/* $FILENAME$ created by $USERNAME$ on $DATE$ */
+
+#import <Foundation/Foundation.h>
+
+@interface $FILENAMESANSEXTENSION$ : NSObject
+{
+
+}
+
+@end
diff --git a/scutil.tproj/m.template b/scutil.tproj/m.template
new file mode 100644 (file)
index 0000000..1216fe5
--- /dev/null
@@ -0,0 +1,18 @@
+$$ Lines starting with $$ are not inserted into newly created files
+$$ The following substitutions are made:
+$$
+$$ $FILENAME$                e.g. foo.m
+$$ $FILENAMESANSEXTENSION$   e.g. foo
+$$ $DIRECTORY$               e.g. /tmp/MyNewApp
+$$ $PROJECTNAME$             e.g. MyNewApp
+$$ $SUBPROJECTNAME$          e.g. TheGoodPart.subproj
+$$ $USERNAME$                e.g. mwagner
+$$ $DATE$                    e.g. Jan-1-1994
+$$
+/* $FILENAME$ created by $USERNAME$ on $DATE$ */
+
+#import "$FILENAMESANSEXTENSION$.h"
+
+@implementation $FILENAMESANSEXTENSION$
+
+@end
diff --git a/scutil.tproj/notify.c b/scutil.tproj/notify.c
new file mode 100644 (file)
index 0000000..b19df55
--- /dev/null
@@ -0,0 +1,352 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include "scutil.h"
+
+static int             osig;
+static struct sigaction        *oact = NULL;
+
+void
+do_notify_list(int argc, char **argv)
+{
+       int             regexOptions = 0;
+       SCDStatus       status;
+       CFArrayRef      list;
+       CFIndex         listCnt;
+       int             i;
+
+       if (argc == 1)
+               regexOptions = kSCDRegexKey;
+
+       status = SCDNotifierList(session, regexOptions, &list);
+       if (status != SCD_OK) {
+               printf("SCDNotifierList: %s\n", SCDError(status));
+               return;
+       }
+
+       listCnt = CFArrayGetCount(list);
+       if (listCnt > 0) {
+               for (i=0; i<listCnt; i++) {
+                       SCDLog(LOG_NOTICE, CFSTR("  notifierKey [%d] = %@"), i, CFArrayGetValueAtIndex(list, i));
+               }
+       } else {
+               SCDLog(LOG_NOTICE, CFSTR("  no notifierKey's"));
+       }
+       CFRelease(list);
+
+       return;
+}
+
+
+void
+do_notify_add(int argc, char **argv)
+{
+       CFStringRef     key;
+       int             regexOptions = 0;
+       SCDStatus       status;
+
+       key = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingMacRoman);
+
+       if (argc == 2)
+               regexOptions = kSCDRegexKey;
+
+       status = SCDNotifierAdd(session, key, regexOptions);
+       CFRelease(key);
+       if (status != SCD_OK) {
+               printf("SCDNotifierAdd: %s\n", SCDError(status));
+       }
+       return;
+}
+
+
+void
+do_notify_remove(int argc, char **argv)
+{
+       SCDStatus       status;
+       CFStringRef     key;
+       int             regexOptions = 0;
+
+       key   = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingMacRoman);
+
+       if (argc == 2)
+               regexOptions = kSCDRegexKey;
+
+       status = SCDNotifierRemove(session, key, regexOptions);
+       CFRelease(key);
+       if (status != SCD_OK) {
+               printf("SCDNotifierRemove: %s\n", SCDError(status));
+       }
+       return;
+}
+
+
+void
+do_notify_changes(int argc, char **argv)
+{
+       CFArrayRef      list;
+       CFIndex         listCnt;
+       SCDStatus       status;
+       int             i;
+
+       status = SCDNotifierGetChanges(session, &list);
+       if (status != SCD_OK) {
+               printf("SCDNotifierGetChanges: %s\n", SCDError(status));
+               return;
+       }
+
+       listCnt = CFArrayGetCount(list);
+       if (listCnt > 0) {
+               for (i=0; i<listCnt; i++) {
+                       SCDLog(LOG_NOTICE, CFSTR("  changedKey [%d] = %@"), i, CFArrayGetValueAtIndex(list, i));
+               }
+       } else {
+               SCDLog(LOG_NOTICE, CFSTR("  no changedKey's"));
+       }
+       CFRelease(list);
+
+       return;
+}
+
+
+void
+do_notify_wait(int argc, char **argv)
+{
+       SCDStatus       status;
+
+       status = SCDNotifierWait(session);
+       if (status != SCD_OK) {
+               printf("SCDNotifierWait: %s\n", SCDError(status));
+               return;
+       }
+
+       printf("OK, something changed!\n");
+       return;
+}
+
+
+static boolean_t
+notificationWatcher(SCDSessionRef session, void *arg)
+{
+       printf("notification callback (session address = %p)\n", session);
+       printf("  arg = %s\n", (char *)arg);
+       return TRUE;
+}
+
+
+static boolean_t
+notificationWatcherVerbose(SCDSessionRef session, void *arg)
+{
+       printf("notification callback (session address = %p)\n", session);
+       printf("  arg = %s\n", (char *)arg);
+       do_notify_changes(0, NULL);     /* report the keys which changed */
+       return TRUE;
+}
+
+
+void
+do_notify_callback(int argc, char **argv)
+{
+       SCDStatus               status;
+       SCDCallbackRoutine_t    func  = notificationWatcher;
+
+       if ((argc == 1) && (strcmp(argv[0], "verbose") == 0)) {
+               func = notificationWatcherVerbose;
+       }
+
+       status = SCDNotifierInformViaCallback(session,
+                                             func,
+                                             "Changed detected by callback handler!");
+       if (status != SCD_OK) {
+               printf("SCDNotifierInformViaCallback: %s\n", SCDError(status));
+               return;
+       }
+
+       return;
+}
+
+
+void
+do_notify_file(int argc, char **argv)
+{
+       int32_t         reqID = 0;
+       SCDStatus       status;
+       int             fd;
+       union {
+               char    data[4];
+               int32_t gotID;
+       } buf;
+       char            *bufPtr;
+       int             needed;
+
+       if (argc == 1) {
+               if ((sscanf(argv[0], "%d", &reqID) != 1)) {
+                       printf("invalid identifier\n");
+                       return;
+               }
+       }
+
+       status = SCDNotifierInformViaFD(session, reqID, &fd);
+       if (status != SCD_OK) {
+               printf("SCDNotifierInformViaFD: %s\n", SCDError(status));
+               return;
+       }
+
+       bzero(buf.data, sizeof(buf.data));
+       bufPtr = &buf.data[0];
+       needed = sizeof(buf.gotID);
+       while (needed > 0) {
+               int     got;
+
+               got = read(fd, bufPtr, needed);
+               if (got == -1) {
+                       /* if error detected */
+                       printf("read() failed: %s\n", strerror(errno));
+                       break;
+               }
+
+               if (got == 0) {
+                       /* if end of file detected */
+                       printf("read(): detected end of file\n");
+                       break;
+               }
+
+               printf("Received %d bytes\n", got);
+               bufPtr += got;
+               needed -= got;
+       }
+
+       if (needed != sizeof(buf.gotID)) {
+               printf("  Received notification, identifier = %d\n", buf.gotID);
+       }
+
+       /* this utility only allows processes one notification per "n.file" request */
+       (void)SCDNotifierCancel(session);
+
+       (void) close(fd);       /* close my side of the file descriptor */
+
+       return;
+}
+
+
+static char *signames[] = {
+       ""    , "HUP" , "INT"   , "QUIT", "ILL"  , "TRAP", "ABRT", "EMT" ,
+       "FPE" , "KILL", "BUS"   , "SEGV", "SYS"  , "PIPE", "ALRM", "TERM",
+       "URG" , "STOP", "TSTP"  , "CONT", "CHLD" , "TTIN", "TTOU", "IO"  ,
+       "XCPU", "XFSZ", "VTALRM", "PROF", "WINCH", "INFO", "USR1",
+       "USR2"
+};
+
+
+static void
+signalCatcher(int signum)
+{
+       static int      n = 0;
+
+       printf("Received SIG%s (#%d)\n", signames[signum], n++);
+       return;
+}
+
+
+void
+do_notify_signal(int argc, char **argv)
+{
+       int                     sig;
+       pid_t                   pid;
+       struct sigaction        nact;
+       int                     ret;
+       SCDStatus               status;
+
+       if (isdigit(*argv[0])) {
+               if ((sscanf(argv[0], "%d", &sig) != 1) || (sig <= 0) || (sig >= NSIG)) {
+                       printf("signal must be in the range of 1 .. %d\n", NSIG-1);
+                       return;
+               }
+       } else {
+               for (sig=1; sig<NSIG; sig++) {
+                       if (strcasecmp(argv[0], signames[sig]) == 0)
+                               break;
+               }
+               if (sig >= NSIG) {
+                       printf("Signal must be one of the following:");
+                       for (sig=1; sig<NSIG; sig++) {
+                               if ((sig % 10) == 1)
+                                       printf("\n ");
+                               printf(" %-6s", signames[sig]);
+                       }
+                       printf("\n");
+                       return;
+               }
+
+       }
+
+       if ((argc != 2) || (sscanf(argv[1], "%d", &pid) != 1)) {
+               pid = getpid();
+       }
+
+       if (oact != NULL) {
+               ret = sigaction(osig, oact, NULL);      /* restore original signal handler */
+       } else {
+               oact = malloc(sizeof(struct sigaction));
+       }
+
+       nact.sa_handler = signalCatcher;
+       sigemptyset(&nact.sa_mask);
+       nact.sa_flags = SA_RESTART;
+       ret = sigaction(sig, &nact, oact);
+       osig = sig;
+       printf("signal handler started\n");
+
+       status = SCDNotifierInformViaSignal(session, pid, sig);
+       if (status != SCD_OK) {
+               printf("SCDNotifierInformViaSignal: %s\n", SCDError(status));
+               return;
+       }
+
+       return;
+}
+
+
+void
+do_notify_cancel(int argc, char **argv)
+{
+       SCDStatus               status;
+       int                     ret;
+
+       status = SCDNotifierCancel(session);
+       if (status != SCD_OK) {
+               printf("SCDNotifierCancel: %s\n", SCDError(status));
+               return;
+       }
+
+       if (oact != NULL) {
+               ret = sigaction(osig, oact, NULL);      /* restore original signal handler */
+               free(oact);
+               oact = NULL;
+       }
+
+       return;
+}
diff --git a/scutil.tproj/notify.h b/scutil.tproj/notify.h
new file mode 100644 (file)
index 0000000..6a353ff
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#ifndef _NOTIFY_H
+#define _NOTIFY_H
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+void   do_notify_list          __P((int argc, char **argv));
+void   do_notify_add           __P((int argc, char **argv));
+void   do_notify_remove        __P((int argc, char **argv));
+void   do_notify_changes       __P((int argc, char **argv));
+void   do_notify_wait          __P((int argc, char **argv));
+void   do_notify_callback      __P((int argc, char **argv));
+void   do_notify_signal        __P((int argc, char **argv));
+void   do_notify_file          __P((int argc, char **argv));
+void   do_notify_cancel        __P((int argc, char **argv));
+
+__END_DECLS
+
+#endif /* !_NOTIFY_H */
diff --git a/scutil.tproj/scutil.c b/scutil.tproj/scutil.c
new file mode 100644 (file)
index 0000000..b46c128
--- /dev/null
@@ -0,0 +1,293 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sysexits.h>
+
+#ifdef DEBUG
+#include <mach/mach.h>
+#include <mach/mach_error.h>
+#endif /* DEBUG */
+
+#include "scutil.h"
+#include "SCDPrivate.h"
+#include "commands.h"
+#include "dictionary.h"
+
+#define LINE_LENGTH 256
+
+SCDSessionRef          session = NULL;
+SCDHandleRef           data    = NULL;
+int                    nesting = 0;
+CFMutableArrayRef      sources = NULL;
+
+
+static char *
+getLine(char *buf, int len, FILE *fp)
+{
+       int x;
+
+       if (fgets(buf, len, fp) == NULL)
+               return NULL;
+
+       x = strlen(buf);
+       if (buf[x-1] == '\n') {
+               /* the entire line fit in the buffer, remove the newline */
+               buf[x-1] = '\0';
+       } else {
+               /* eat the remainder of the line */
+               do {
+                       x = fgetc(fp);
+               } while ((x != '\n') && (x != EOF));
+       }
+
+       return buf;
+}
+
+
+char *
+getString(char **line)
+{
+       char *s, *e, c, *string;
+       int i, isQuoted = 0, escaped = 0;
+
+       if (*line == NULL) return NULL;
+       if (**line == '\0') return NULL;
+
+       /* Skip leading white space */
+       while (isspace(**line)) *line += 1;
+
+       /* Grab the next string */
+       s = *line;
+       if (*s == '\0') {
+               return NULL;                            /* no string available */
+       } else if (*s == '"') {
+               isQuoted = 1;                           /* it's a quoted string */
+               s++;
+       }
+
+       for (e = s; (c = *e) != '\0'; e++) {
+               if (isQuoted && (c == '"'))
+                       break;                          /* end of quoted string */
+               if (c == '\\') {
+                       e++;
+                       if (*e == '\0')
+                               break;                  /* if premature end-of-string */
+                       if ((*e == '"') || isspace(*e))
+                               escaped++;              /* if escaped quote or white space */
+               }
+               if (!isQuoted && isspace(c))
+                       break;                          /* end of non-quoted string */
+       }
+
+       string = malloc(e - s - escaped + 1);
+
+       for (i = 0; s < e; s++) {
+               string[i] = *s;
+               if (!((s[0] == '\\') && ((s[1] == '"') || isspace(s[1])))) i++;
+       }
+       string[i] = '\0';
+
+       if (isQuoted)
+               e++;                                    /* move past end of quoted string */
+
+       *line = e;
+       return string;
+}
+
+
+boolean_t
+process_line(FILE *fp)
+{
+       char    line[LINE_LENGTH], *s, *arg, **argv = NULL;
+       int     i, argc;
+
+       /* if end-of-file, exit */
+       if (getLine(line, sizeof(line), fp) == NULL)
+               return FALSE;
+
+       if ((nesting > 0) && SCDOptionGet(NULL, kSCDOptionVerbose)) {
+               SCDLog(LOG_NOTICE, CFSTR("%d> %s"), nesting, line);
+       }
+
+       /* if requested, exit */
+       if (strcasecmp(line, "exit") == 0) return FALSE;
+       if (strcasecmp(line, "quit") == 0) return FALSE;
+       if (strcasecmp(line, "q"   ) == 0) return FALSE;
+
+       /* break up the input line */
+       s = line;
+       argc = 0;
+       while ((arg = getString(&s)) != NULL) {
+               if (argc == 0)
+                       argv = (char **)malloc(2 * sizeof(char *));
+               else
+                       argv = (char **)realloc(argv, ((argc + 2) * sizeof(char *)));
+               argv[argc++] = arg;
+       }
+
+       /* process the command */
+       if (argc > 0) {
+               argv[argc] = NULL;      /* just in case... */
+
+               if (*argv[0] != '#')
+                       do_command(argc, argv);
+
+               for (i = 0; i < argc; i++)
+                       free(argv[i]);
+               free(argv);
+       }
+
+       return TRUE;
+}
+
+
+void
+runLoopProcessInput(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info)
+{
+       FILE    *fp = info;
+
+       if (process_line(fp) == FALSE) {
+               /* we don't want any more input from this stream, stop listening */
+               CFRunLoopRemoveSource(CFRunLoopGetCurrent(),
+                                     (CFRunLoopSourceRef)CFArrayGetValueAtIndex(sources, 0),
+                                     kCFRunLoopDefaultMode);
+
+               /* we no longer need the fd (socket) */
+               CFSocketInvalidate(s);
+
+               /* we no longer need to track this source */
+               CFArrayRemoveValueAtIndex(sources, 0);
+
+               if (CFArrayGetCount(sources) > 0) {
+                       /* add the previous input source to the run loop */
+                       CFRunLoopAddSource(CFRunLoopGetCurrent(),
+                                          (CFRunLoopSourceRef)CFArrayGetValueAtIndex(sources, 0),
+                                          kCFRunLoopDefaultMode);
+               } else {
+                       /* no more input sources, we're done! */
+                       exit (EX_OK);
+               }
+
+               /* decrement the nesting level */
+               nesting--;
+       }
+
+       if (SCDOptionGet(NULL, kSCDOptionUseCFRunLoop)) {
+               /* debug information, diagnostics */
+               _showMachPortStatus();
+
+               /* if necessary, re-issue prompt */
+               if ((CFArrayGetCount(sources) == 1) && isatty(STDIN_FILENO)) {
+                       printf("> ");
+                       fflush(stdout);
+               }
+       }
+
+       return;
+}
+
+
+int
+main(int argc, const char *argv[])
+{
+       extern int      optind;
+       int             opt;
+       boolean_t       ok;
+
+       /* process any arguments */
+
+       SCDOptionSet(NULL, kSCDOptionUseCFRunLoop, FALSE);
+
+       while ((opt = getopt(argc, argv, "dvr")) != -1)
+               switch(opt) {
+               case 'd':
+                       SCDOptionSet(NULL, kSCDOptionDebug, TRUE);
+                       break;
+               case 'v':
+                       SCDOptionSet(NULL, kSCDOptionVerbose, TRUE);
+                       break;
+               case 'r':
+                       SCDOptionSet(NULL, kSCDOptionUseCFRunLoop, TRUE);
+                       break;
+               case '?':
+               default :
+                       do_help(0, NULL);
+       }
+       argc -= optind;
+       argv += optind;
+
+       /* start with an empty dictionary */
+       do_dictInit(0, NULL);
+
+       if (SCDOptionGet(NULL, kSCDOptionUseCFRunLoop)) {
+               CFSocketRef             in;
+               CFSocketContext         context = { 0, stdin, NULL, NULL, NULL };
+               CFRunLoopSourceRef      rls;
+
+               /* create a "socket" reference with the file descriptor associated with stdin */
+               in  = CFSocketCreateWithNative(NULL,
+                                              STDIN_FILENO,
+                                              kCFSocketReadCallBack,
+                                              runLoopProcessInput,
+                                              &context);
+
+               /* Create a run loop source for the (stdin) file descriptor */
+               rls = CFSocketCreateRunLoopSource(NULL, in, nesting);
+
+               /* keep track of input file sources */
+               sources = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+               CFArrayAppendValue(sources, rls);
+
+               /* add this source to the run loop */
+               CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
+
+               CFRelease(rls);
+               CFRelease(in);
+       }
+
+       do {
+               /* debug information, diagnostics */
+               _showMachPortStatus();
+
+               /* issue prompt */
+               if (isatty(STDIN_FILENO)) {
+                       printf("> ");
+                       fflush(stdout);
+               }
+
+               if (SCDOptionGet(NULL, kSCDOptionUseCFRunLoop)) {
+                       CFRunLoopRun(); /* process input, process events */
+                       ok = FALSE;     /* if the RunLoop exited */
+               } else {
+                       /* process command */
+                       ok = process_line(stdin);
+               }
+       } while (ok);
+
+       exit (EX_OK);   // insure the process exit status is 0
+       return 0;       // ...and make main fit the ANSI spec.
+}
diff --git a/scutil.tproj/scutil.h b/scutil.tproj/scutil.h
new file mode 100644 (file)
index 0000000..b1d13dd
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#ifndef _SC_H
+#define _SC_H
+
+#include <sys/cdefs.h>
+
+#include <SystemConfiguration/SCD.h>
+
+extern SCDSessionRef           session;
+extern SCDHandleRef            data;
+extern int                     nesting;
+extern CFMutableArrayRef       sources;
+
+__BEGIN_DECLS
+
+boolean_t      process_line            __P((FILE       *fp));
+
+void           runLoopProcessInput     __P((CFSocketRef                s,
+                                            CFSocketCallBackType       type,
+                                            CFDataRef                  address,
+                                            const void                 *data,
+                                            void                       *info));
+
+__END_DECLS
+
+#endif /* !_SC_H */
diff --git a/scutil.tproj/session.c b/scutil.tproj/session.c
new file mode 100644 (file)
index 0000000..7a8287e
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include "scutil.h"
+
+void
+do_open(int argc, char **argv)
+{
+       SCDStatus       status;
+
+       if (session != NULL) {
+               status = SCDClose(&session);
+               switch (status) {
+                       case SCD_OK :
+                       case SCD_NOSESSION :
+                               /*
+                                * if the "close" was successful or if we had an open
+                                * session but can no talk to the server
+                                */
+                               break;
+                       default :
+                               SCDLog(LOG_INFO, CFSTR("SCDClose: %s"), SCDError(status));
+                               return;
+               }
+       }
+
+       status = SCDOpen(&session, CFSTR("sc"));
+       if (status != SCD_OK) {
+               SCDLog(LOG_INFO, CFSTR("SCDOpen: %s"), SCDError(status));
+               return;
+       }
+
+       return;
+}
+
+
+void
+do_close(int argc, char **argv)
+{
+       SCDStatus       status;
+
+       status = SCDClose(&session);
+       if (status != SCD_OK) {
+               SCDLog(LOG_INFO, CFSTR("SCDClose: %s"), SCDError(status));
+       }
+       return;
+}
+
+
+void
+do_lock(int argc, char **argv)
+{
+       SCDStatus       status;
+
+       status = SCDLock(session);
+       if (status != SCD_OK) {
+               SCDLog(LOG_INFO, CFSTR("SCDLock: %s"), SCDError(status));
+       }
+       return;
+}
+
+
+void
+do_unlock(int argc, char **argv)
+{
+       SCDStatus       status;
+
+       status = SCDUnlock(session);
+       if (status != SCD_OK) {
+               SCDLog(LOG_INFO, CFSTR("SCDUnlock: %s"), SCDError(status));
+       }
+       return;
+}
diff --git a/scutil.tproj/session.h b/scutil.tproj/session.h
new file mode 100644 (file)
index 0000000..8a3f8bb
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#ifndef _SESSION_H
+#define _SESSION_H
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+void   do_open                 __P((int argc, char **argv));
+void   do_close                __P((int argc, char **argv));
+void   do_lock                 __P((int argc, char **argv));
+void   do_unlock               __P((int argc, char **argv));
+
+__END_DECLS
+
+#endif /* !_SESSION_H */
diff --git a/scutil.tproj/tests.c b/scutil.tproj/tests.c
new file mode 100644 (file)
index 0000000..703ef5f
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#include "scutil.h"
+#include "SCDPrivate.h"
+
+void
+do_snapshot(int argc, char **argv)
+{
+       SCDStatus       status;
+
+       status = SCDSnapshot(session);
+       if (status != SCD_OK) {
+               printf("SCDSnapshot: %s\n", SCDError(status));
+       }
+       return;
+}
+
+
+#ifdef DEBUG
+
+void
+test_openCloseLeak(int argc, char **argv)
+{
+       SCDStatus       status;
+       int             i, loopCnt;
+       SCDSessionRef   *sessions;
+
+       if ((argc == 0) || (sscanf(argv[0], "%d", &loopCnt) != 1)) {
+               loopCnt = 100;
+       }
+
+       sessions = malloc(loopCnt * sizeof(SCDSessionRef));
+
+       /* open, close, open, close, open, close, ... */
+       for (i=0; i<loopCnt; i++) {
+               status = SCDOpen(&sessions[i], CFSTR("sc"));
+               if (status != SCD_OK) {
+                       printf("SCDOpen: %s\n", SCDError(status));
+                       break;
+               }
+
+               status = SCDClose(&sessions[i]);
+               if (status != SCD_OK) {
+                       printf("SCDClose: %s\n", SCDError(status));
+                       break;
+               }
+       }
+
+       /* open, open, open, close, close, close, ... */
+       for (i=0; i<loopCnt; i++) {
+               status = SCDOpen(&sessions[i], CFSTR("sc"));
+               if (status != SCD_OK) {
+                       printf("SCDOpen: %s\n", SCDError(status));
+                       break;
+               }
+       }
+       for (i=0; i<loopCnt; i++) {
+               status = SCDClose(&sessions[i]);
+               if (status != SCD_OK) {
+                       printf("SCDClose: %s\n", SCDError(status));
+                       break;
+               }
+       }
+
+       return;
+}
+#endif /* DEBUG */
diff --git a/scutil.tproj/tests.h b/scutil.tproj/tests.h
new file mode 100644 (file)
index 0000000..38d9b9b
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2000 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@
+ */
+
+#ifndef _TESTS_H
+#define _TESTS_H
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+void   do_snapshot             __P((int argc, char **argv));
+
+#ifdef DEBUG
+void   test_setServer          __P((int argc, char **argv));
+void   test_openCloseLeak      __P((int argc, char **argv));
+#endif /* DEBUG */
+
+__END_DECLS
+
+#endif /* !_TESTS_H */