From ed34e3c3e5fb80e0702ac7fb92f189862089d820 Mon Sep 17 00:00:00 2001 From: Apple Date: Mon, 7 Aug 2006 20:47:51 +0000 Subject: [PATCH] launchd-152.tar.gz --- launchd/COPYING | 569 ++- launchd/aclocal.m4 | 2 +- launchd/configure | 2923 ++++++++++++---- launchd/configure.ac | 31 +- launchd/doc/Makefile.am | 3 - launchd/doc/Makefile.in | 203 -- launchd/doc/com.apple.launchdebugd.xml | 30 - launchd/doc/sampled.c | 24 +- launchd/doc/sampled.plist | 40 + launchd/src/ConsoleMessage.8 | 105 - launchd/src/ConsoleMessage.c | 327 -- launchd/src/IPC.c | 41 +- launchd/src/IPC.h | 28 +- launchd/src/Makefile.am | 75 +- launchd/src/Makefile.in | 406 ++- launchd/src/StartupItemContext | 5 + launchd/src/StartupItemContext.c | 99 - launchd/src/StartupItems.c | 581 ++-- launchd/src/StartupItems.h | 43 +- launchd/src/StartupItems/Apache | 52 - launchd/src/StartupItems/Apache.plist | 5 - launchd/src/StartupItems/AppServices | 27 - launchd/src/StartupItems/AppServices.plist | 4 - launchd/src/StartupItems/AppleShare | 2 + launchd/src/StartupItems/AuthServer | 31 - launchd/src/StartupItems/AuthServer.plist | 4 - launchd/src/StartupItems/IPServices | 2 + launchd/src/StartupItems/NFS | 126 - launchd/src/StartupItems/NFS.plist | 5 - launchd/src/StartupItems/NetworkTime | 42 - launchd/src/StartupItems/NetworkTime.plist | 4 - launchd/src/SystemStarter.c | 36 +- launchd/src/SystemStarter.h | 28 +- launchd/src/SystemStarterIPC.h | 28 +- launchd/src/bootstrap.c | 1074 ------ launchd/src/bootstrap.defs | 350 +- launchd/src/bootstrap_internal.h | 60 - launchd/src/bootstrap_private.h | 55 + launchd/src/bootstrap_public.c | 285 ++ launchd/src/bootstrap_public.h | 321 ++ launchd/src/hostconfig | 4 +- launchd/src/init.8 | 295 -- launchd/src/init.c | 277 +- launchd/src/launch.h | 144 +- launchd/src/launch_priv.h | 120 +- launchd/src/launchctl.1 | 59 +- launchd/src/launchctl.c | 1524 ++++++-- launchd/src/launchd.8 | 30 +- launchd/src/launchd.c | 2633 +++----------- launchd/src/launchd.h | 94 +- launchd/src/launchd.plist.5 | 138 +- launchd/src/launchd_core_logic.c | 3059 +++++++++++++++++ launchd/src/launchd_core_logic.h | 85 + launchd/src/launchd_debugd.8 | 21 - launchd/src/launchd_internal.defs | 33 + launchd/src/launchd_mach_ipc.c | 916 +++++ launchd/src/launchd_unix_ipc.c | 523 +++ launchd/src/launchd_unix_ipc.h | 42 + launchd/src/launchdebugd.c | 182 - launchd/src/launchproxy.c | 50 +- launchd/src/liblaunch.c | 472 ++- launchd/src/lists.c | 485 --- launchd/src/lists.h | 155 - launchd/src/mpm_reply.defs | 61 + launchd/src/rc | 302 -- launchd/src/rc.8 | 109 +- launchd/src/rc.common | 2 + launchd/src/rc.netboot | 44 +- launchd/src/register_mach_bootstrap_servers.c | 169 - launchd/src/rpc_services.c | 846 ----- launchd/src/service | 2 + launchd/src/wait4path.c | 25 +- launchd/testing/EVFILT_TIMER.c | 50 + ...4\261c\304\270s\304\261lv\316\265\312\200" | 3 + launchd/testing/loginwindow_helper_code.c | 38 + launchd/testing/mach_via_launchd.c | 47 + launchd/testing/mach_via_launchd.plist | 19 + launchd/testing/missed-EVFILT_READ.c | 112 + launchd/testing/missed-fds.c | 186 + launchd/testing/spawn_via_launchd.c | 53 + 80 files changed, 11763 insertions(+), 9722 deletions(-) delete mode 100644 launchd/doc/Makefile.am delete mode 100644 launchd/doc/Makefile.in delete mode 100644 launchd/doc/com.apple.launchdebugd.xml create mode 100644 launchd/doc/sampled.plist delete mode 100644 launchd/src/ConsoleMessage.8 delete mode 100644 launchd/src/ConsoleMessage.c create mode 100755 launchd/src/StartupItemContext delete mode 100644 launchd/src/StartupItemContext.c delete mode 100644 launchd/src/StartupItems/Apache delete mode 100644 launchd/src/StartupItems/Apache.plist delete mode 100644 launchd/src/StartupItems/AppServices delete mode 100644 launchd/src/StartupItems/AppServices.plist delete mode 100644 launchd/src/StartupItems/AuthServer delete mode 100644 launchd/src/StartupItems/AuthServer.plist delete mode 100644 launchd/src/StartupItems/NFS delete mode 100644 launchd/src/StartupItems/NFS.plist delete mode 100644 launchd/src/StartupItems/NetworkTime delete mode 100644 launchd/src/StartupItems/NetworkTime.plist delete mode 100644 launchd/src/bootstrap.c delete mode 100644 launchd/src/bootstrap_internal.h create mode 100644 launchd/src/bootstrap_private.h create mode 100644 launchd/src/bootstrap_public.c create mode 100644 launchd/src/bootstrap_public.h delete mode 100644 launchd/src/init.8 create mode 100644 launchd/src/launchd_core_logic.c create mode 100644 launchd/src/launchd_core_logic.h delete mode 100644 launchd/src/launchd_debugd.8 create mode 100644 launchd/src/launchd_internal.defs create mode 100644 launchd/src/launchd_mach_ipc.c create mode 100644 launchd/src/launchd_unix_ipc.c create mode 100644 launchd/src/launchd_unix_ipc.h delete mode 100644 launchd/src/launchdebugd.c delete mode 100644 launchd/src/lists.c delete mode 100644 launchd/src/lists.h create mode 100644 launchd/src/mpm_reply.defs delete mode 100644 launchd/src/rc delete mode 100644 launchd/src/register_mach_bootstrap_servers.c delete mode 100644 launchd/src/rpc_services.c create mode 100644 launchd/testing/EVFILT_TIMER.c create mode 100755 "launchd/testing/Qu\304\261c\304\270s\304\261lv\316\265\312\200" create mode 100644 launchd/testing/loginwindow_helper_code.c create mode 100644 launchd/testing/mach_via_launchd.c create mode 100644 launchd/testing/mach_via_launchd.plist create mode 100644 launchd/testing/missed-EVFILT_READ.c create mode 100644 launchd/testing/missed-fds.c create mode 100644 launchd/testing/spawn_via_launchd.c diff --git a/launchd/COPYING b/launchd/COPYING index fe81a60..d645695 100644 --- a/launchd/COPYING +++ b/launchd/COPYING @@ -1,367 +1,202 @@ -APPLE PUBLIC SOURCE LICENSE -Version 2.0 - August 6, 2003 - -Please read this License carefully before downloading this software. -By downloading or using this software, you are agreeing to be bound by -the terms of this License. If you do not or cannot agree to the terms -of this License, please do not download or use the software. - -1. General; Definitions. This License applies to any program or other -work which Apple Computer, Inc. ("Apple") makes publicly available and -which contains a notice placed by Apple identifying such program or -work as "Original Code" and stating that it is subject to the terms of -this Apple Public Source License version 2.0 ("License"). As used in -this License: - -1.1 "Applicable Patent Rights" mean: (a) in the case where Apple is -the grantor of rights, (i) claims of patents that are now or hereafter -acquired, owned by or assigned to Apple and (ii) that cover subject -matter contained in the Original Code, but only to the extent -necessary to use, reproduce and/or distribute the Original Code -without infringement; and (b) in the case where You are the grantor of -rights, (i) claims of patents that are now or hereafter acquired, -owned by or assigned to You and (ii) that cover subject matter in Your -Modifications, taken alone or in combination with Original Code. - -1.2 "Contributor" means any person or entity that creates or -contributes to the creation of Modifications. - -1.3 "Covered Code" means the Original Code, Modifications, the -combination of Original Code and any Modifications, and/or any -respective portions thereof. - -1.4 "Externally Deploy" means: (a) to sublicense, distribute or -otherwise make Covered Code available, directly or indirectly, to -anyone other than You; and/or (b) to use Covered Code, alone or as -part of a Larger Work, in any way to provide a service, including but -not limited to delivery of content, through electronic communication -with a client other than You. - -1.5 "Larger Work" means a work which combines Covered Code or portions -thereof with code not governed by the terms of this License. - -1.6 "Modifications" mean any addition to, deletion from, and/or change -to, the substance and/or structure of the Original Code, any previous -Modifications, the combination of Original Code and any previous -Modifications, and/or any respective portions thereof. When code is -released as a series of files, a Modification is: (a) any addition to -or deletion from the contents of a file containing Covered Code; -and/or (b) any new file or other representation of computer program -statements that contains any part of Covered Code. - -1.7 "Original Code" means (a) the Source Code of a program or other -work as originally made available by Apple under this License, -including the Source Code of any updates or upgrades to such programs -or works made available by Apple under this License, and that has been -expressly identified by Apple as such in the header file(s) of such -work; and (b) the object code compiled from such Source Code and -originally made available by Apple under this License. - -1.8 "Source Code" means the human readable form of a program or other -work that is suitable for making modifications to it, including all -modules it contains, plus any associated interface definition files, -scripts used to control compilation and installation of an executable -(object code). - -1.9 "You" or "Your" means an individual or a legal entity exercising -rights under this License. For legal entities, "You" or "Your" -includes any entity which controls, is controlled by, or is under -common control with, You, where "control" means (a) the power, direct -or indirect, to cause the direction or management of such entity, -whether by contract or otherwise, or (b) ownership of fifty percent -(50%) or more of the outstanding shares or beneficial ownership of -such entity. - -2. Permitted Uses; Conditions & Restrictions. Subject to the terms -and conditions of this License, Apple hereby grants You, effective on -the date You accept this License and download the Original Code, a -world-wide, royalty-free, non-exclusive license, to the extent of -Apple's Applicable Patent Rights and copyrights covering the Original -Code, to do the following: - -2.1 Unmodified Code. You may use, reproduce, display, perform, -internally distribute within Your organization, and Externally Deploy -verbatim, unmodified copies of the Original Code, for commercial or -non-commercial purposes, provided that in each instance: - -(a) You must retain and reproduce in all copies of Original Code the -copyright and other proprietary notices and disclaimers of Apple as -they appear in the Original Code, and keep intact all notices in the -Original Code that refer to this License; and - -(b) You must include a copy of this License with every copy of Source -Code of Covered Code and documentation You distribute or Externally -Deploy, and You may not offer or impose any terms on such Source Code -that alter or restrict this License or the recipients' rights -hereunder, except as permitted under Section 6. - -2.2 Modified Code. You may modify Covered Code and use, reproduce, -display, perform, internally distribute within Your organization, and -Externally Deploy Your Modifications and Covered Code, for commercial -or non-commercial purposes, provided that in each instance You also -meet all of these conditions: - -(a) You must satisfy all the conditions of Section 2.1 with respect to -the Source Code of the Covered Code; - -(b) You must duplicate, to the extent it does not already exist, the -notice in Exhibit A in each file of the Source Code of all Your -Modifications, and cause the modified files to carry prominent notices -stating that You changed the files and the date of any change; and - -(c) If You Externally Deploy Your Modifications, You must make -Source Code of all Your Externally Deployed Modifications either -available to those to whom You have Externally Deployed Your -Modifications, or publicly available. Source Code of Your Externally -Deployed Modifications must be released under the terms set forth in -this License, including the license grants set forth in Section 3 -below, for as long as you Externally Deploy the Covered Code or twelve -(12) months from the date of initial External Deployment, whichever is -longer. You should preferably distribute the Source Code of Your -Externally Deployed Modifications electronically (e.g. download from a -web site). - -2.3 Distribution of Executable Versions. In addition, if You -Externally Deploy Covered Code (Original Code and/or Modifications) in -object code, executable form only, You must include a prominent -notice, in the code itself as well as in related documentation, -stating that Source Code of the Covered Code is available under the -terms of this License with information on how and where to obtain such -Source Code. - -2.4 Third Party Rights. You expressly acknowledge and agree that -although Apple and each Contributor grants the licenses to their -respective portions of the Covered Code set forth herein, no -assurances are provided by Apple or any Contributor that the Covered -Code does not infringe the patent or other intellectual property -rights of any other entity. Apple and each Contributor disclaim any -liability to You for claims brought by any other entity based on -infringement of intellectual property rights or otherwise. As a -condition to exercising the rights and licenses granted hereunder, You -hereby assume sole responsibility to secure any other intellectual -property rights needed, if any. For example, if a third party patent -license is required to allow You to distribute the Covered Code, it is -Your responsibility to acquire that license before distributing the -Covered Code. - -3. Your Grants. In consideration of, and as a condition to, the -licenses granted to You under this License, You hereby grant to any -person or entity receiving or distributing Covered Code under this -License a non-exclusive, royalty-free, perpetual, irrevocable license, -under Your Applicable Patent Rights and other intellectual property -rights (other than patent) owned or controlled by You, to use, -reproduce, display, perform, modify, sublicense, distribute and -Externally Deploy Your Modifications of the same scope and extent as -Apple's licenses under Sections 2.1 and 2.2 above. - -4. Larger Works. You may create a Larger Work by combining Covered -Code with other code not governed by the terms of this License and -distribute the Larger Work as a single product. In each such instance, -You must make sure the requirements of this License are fulfilled for -the Covered Code or any portion thereof. - -5. Limitations on Patent License. Except as expressly stated in -Section 2, no other patent rights, express or implied, are granted by -Apple herein. Modifications and/or Larger Works may require additional -patent licenses from Apple which Apple may grant in its sole -discretion. - -6. Additional Terms. You may choose to offer, and to charge a fee for, -warranty, support, indemnity or liability obligations and/or other -rights consistent with the scope of the license granted herein -("Additional Terms") to one or more recipients of Covered Code. -However, You may do so only on Your own behalf and as Your sole -responsibility, and not on behalf of Apple or any Contributor. You -must obtain the recipient's agreement that any such Additional Terms -are offered by You alone, and You hereby agree to indemnify, defend -and hold Apple and every Contributor harmless for any liability -incurred by or claims asserted against Apple or such Contributor by -reason of any such Additional Terms. - -7. Versions of the License. Apple may publish revised and/or new -versions of this License from time to time. Each version will be given -a distinguishing version number. Once Original Code has been published -under a particular version of this License, You may continue to use it -under the terms of that version. You may also choose to use such -Original Code under the terms of any subsequent version of this -License published by Apple. No one other than Apple has the right to -modify the terms applicable to Covered Code created under this -License. - -8. NO WARRANTY OR SUPPORT. The Covered Code may contain in whole or in -part pre-release, untested, or not fully tested works. The Covered -Code may contain errors that could cause failures or loss of data, and -may be incomplete or contain inaccuracies. You expressly acknowledge -and agree that use of the Covered Code, or any portion thereof, is at -Your sole and entire risk. THE COVERED CODE IS PROVIDED "AS IS" AND -WITHOUT WARRANTY, UPGRADES OR SUPPORT OF ANY KIND AND APPLE AND -APPLE'S LICENSOR(S) (COLLECTIVELY REFERRED TO AS "APPLE" FOR THE -PURPOSES OF SECTIONS 8 AND 9) AND ALL CONTRIBUTORS EXPRESSLY DISCLAIM -ALL WARRANTIES AND/OR CONDITIONS, EXPRESS OR IMPLIED, INCLUDING, BUT -NOT LIMITED TO, THE IMPLIED WARRANTIES AND/OR CONDITIONS OF -MERCHANTABILITY, OF SATISFACTORY QUALITY, OF FITNESS FOR A PARTICULAR -PURPOSE, OF ACCURACY, OF QUIET ENJOYMENT, AND NONINFRINGEMENT OF THIRD -PARTY RIGHTS. APPLE AND EACH CONTRIBUTOR DOES NOT WARRANT AGAINST -INTERFERENCE WITH YOUR ENJOYMENT OF THE COVERED CODE, THAT THE -FUNCTIONS CONTAINED IN THE COVERED CODE WILL MEET YOUR REQUIREMENTS, -THAT THE OPERATION OF THE COVERED CODE WILL BE UNINTERRUPTED OR -ERROR-FREE, OR THAT DEFECTS IN THE COVERED CODE WILL BE CORRECTED. NO -ORAL OR WRITTEN INFORMATION OR ADVICE GIVEN BY APPLE, AN APPLE -AUTHORIZED REPRESENTATIVE OR ANY CONTRIBUTOR SHALL CREATE A WARRANTY. -You acknowledge that the Covered Code is not intended for use in the -operation of nuclear facilities, aircraft navigation, communication -systems, or air traffic control machines in which case the failure of -the Covered Code could lead to death, personal injury, or severe -physical or environmental damage. - -9. LIMITATION OF LIABILITY. TO THE EXTENT NOT PROHIBITED BY LAW, IN NO -EVENT SHALL APPLE OR ANY CONTRIBUTOR BE LIABLE FOR ANY INCIDENTAL, -SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR RELATING -TO THIS LICENSE OR YOUR USE OR INABILITY TO USE THE COVERED CODE, OR -ANY PORTION THEREOF, WHETHER UNDER A THEORY OF CONTRACT, WARRANTY, -TORT (INCLUDING NEGLIGENCE), PRODUCTS LIABILITY OR OTHERWISE, EVEN IF -APPLE OR SUCH CONTRIBUTOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES AND NOTWITHSTANDING THE FAILURE OF ESSENTIAL PURPOSE OF ANY -REMEDY. SOME JURISDICTIONS DO NOT ALLOW THE LIMITATION OF LIABILITY OF -INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS LIMITATION MAY NOT APPLY -TO YOU. In no event shall Apple's total liability to You for all -damages (other than as may be required by applicable law) under this -License exceed the amount of fifty dollars ($50.00). - -10. Trademarks. This License does not grant any rights to use the -trademarks or trade names "Apple", "Apple Computer", "Mac", "Mac OS", -"QuickTime", "QuickTime Streaming Server" or any other trademarks, -service marks, logos or trade names belonging to Apple (collectively -"Apple Marks") or to any trademark, service mark, logo or trade name -belonging to any Contributor. You agree not to use any Apple Marks in -or as part of the name of products derived from the Original Code or -to endorse or promote products derived from the Original Code other -than as expressly permitted by and in strict compliance at all times -with Apple's third party trademark usage guidelines which are posted -at http://www.apple.com/legal/guidelinesfor3rdparties.html. - -11. Ownership. Subject to the licenses granted under this License, -each Contributor retains all rights, title and interest in and to any -Modifications made by such Contributor. Apple retains all rights, -title and interest in and to the Original Code and any Modifications -made by or on behalf of Apple ("Apple Modifications"), and such Apple -Modifications will not be automatically subject to this License. Apple -may, at its sole discretion, choose to license such Apple -Modifications under this License, or on different terms from those -contained in this License or may choose not to license them at all. - -12. Termination. - -12.1 Termination. This License and the rights granted hereunder will -terminate: - -(a) automatically without notice from Apple if You fail to comply with -any term(s) of this License and fail to cure such breach within 30 -days of becoming aware of such breach; - -(b) immediately in the event of the circumstances described in Section -13.5(b); or - -(c) automatically without notice from Apple if You, at any time during -the term of this License, commence an action for patent infringement -against Apple; provided that Apple did not first commence -an action for patent infringement against You in that instance. - -12.2 Effect of Termination. Upon termination, You agree to immediately -stop any further use, reproduction, modification, sublicensing and -distribution of the Covered Code. All sublicenses to the Covered Code -which have been properly granted prior to termination shall survive -any termination of this License. Provisions which, by their nature, -should remain in effect beyond the termination of this License shall -survive, including but not limited to Sections 3, 5, 8, 9, 10, 11, -12.2 and 13. No party will be liable to any other for compensation, -indemnity or damages of any sort solely as a result of terminating -this License in accordance with its terms, and termination of this -License will be without prejudice to any other right or remedy of -any party. - -13. Miscellaneous. - -13.1 Government End Users. The Covered Code is a "commercial item" as -defined in FAR 2.101. Government software and technical data rights in -the Covered Code include only those rights customarily provided to the -public as defined in this License. This customary commercial license -in technical data and software is provided in accordance with FAR -12.211 (Technical Data) and 12.212 (Computer Software) and, for -Department of Defense purchases, DFAR 252.227-7015 (Technical Data -- -Commercial Items) and 227.7202-3 (Rights in Commercial Computer -Software or Computer Software Documentation). Accordingly, all U.S. -Government End Users acquire Covered Code with only those rights set -forth herein. - -13.2 Relationship of Parties. This License will not be construed as -creating an agency, partnership, joint venture or any other form of -legal association between or among You, Apple or any Contributor, and -You will not represent to the contrary, whether expressly, by -implication, appearance or otherwise. - -13.3 Independent Development. Nothing in this License will impair -Apple's right to acquire, license, develop, have others develop for -it, market and/or distribute technology or products that perform the -same or similar functions as, or otherwise compete with, -Modifications, Larger Works, technology or products that You may -develop, produce, market or distribute. - -13.4 Waiver; Construction. Failure by Apple or any Contributor to -enforce any provision of this License will not be deemed a waiver of -future enforcement of that or any other provision. Any law or -regulation which provides that the language of a contract shall be -construed against the drafter will not apply to this License. - -13.5 Severability. (a) If for any reason a court of competent -jurisdiction finds any provision of this License, or portion thereof, -to be unenforceable, that provision of the License will be enforced to -the maximum extent permissible so as to effect the economic benefits -and intent of the parties, and the remainder of this License will -continue in full force and effect. (b) Notwithstanding the foregoing, -if applicable law prohibits or restricts You from fully and/or -specifically complying with Sections 2 and/or 3 or prevents the -enforceability of either of those Sections, this License will -immediately terminate and You must immediately discontinue any use of -the Covered Code and destroy all copies of it that are in your -possession or control. - -13.6 Dispute Resolution. Any litigation or other dispute resolution -between You and Apple relating to this License shall take place in the -Northern District of California, and You and Apple hereby consent to -the personal jurisdiction of, and venue in, the state and federal -courts within that District with respect to this License. The -application of the United Nations Convention on Contracts for the -International Sale of Goods is expressly excluded. - -13.7 Entire Agreement; Governing Law. This License constitutes the -entire agreement between the parties with respect to the subject -matter hereof. This License shall be governed by the laws of the -United States and the State of California, except that body of -California law concerning conflicts of law. - -Where You are located in the province of Quebec, Canada, the following -clause applies: The parties hereby confirm that they have requested -that this License and all related documents be drafted in English. Les -parties ont exige que le present contrat et tous les documents -connexes soient rediges en anglais. - -EXHIBIT A. - -"Portions Copyright (c) 1999-2003 Apple Computer, Inc. All Rights -Reserved. - -This file contains Original Code and/or Modifications of Original Code -as defined in and that are subject to the Apple Public Source License -Version 2.0 (the 'License'). You may not use this file except in -compliance with the License. Please obtain a copy of the License at -http://www.opensource.apple.com/apsl/ and read it before using this -file. - -The 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, QUIET ENJOYMENT OR NON-INFRINGEMENT. -Please see the License for the specific language governing rights and -limitations under the License." + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/launchd/aclocal.m4 b/launchd/aclocal.m4 index d78e9d0..c6cc64e 100644 --- a/launchd/aclocal.m4 +++ b/launchd/aclocal.m4 @@ -1,4 +1,4 @@ -# aclocal.m4 generated automatically by aclocal 1.6.3 -*- Autoconf -*- +# aclocal.m4t generated automatically by aclocal 1.6.3 -*- Autoconf -*- # Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002 # Free Software Foundation, Inc. diff --git a/launchd/configure b/launchd/configure index 546984a..3fa2943 100755 --- a/launchd/configure +++ b/launchd/configure @@ -273,7 +273,7 @@ PACKAGE_VERSION='1.0' PACKAGE_STRING='launchd 1.0' PACKAGE_BUGREPORT='launchd-bug-reports@group.apple.com' -ac_unique_file="src/ConsoleMessage.c" +ac_unique_file="src/launchd.c" # Factoring default headers for most tests. ac_includes_default="\ #include @@ -311,7 +311,7 @@ ac_includes_default="\ # include #endif" -ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA PACKAGE VERSION ACLOCAL AUTOCONF AUTOMAKE AUTOHEADER MAKEINFO AMTAR install_sh STRIP ac_ct_STRIP INSTALL_STRIP_PROGRAM AWK SET_MAKE CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT DEPDIR am__include am__quote AMDEP_TRUE AMDEP_FALSE AMDEPBACKSLASH CCDEPMODE RANLIB ac_ct_RANLIB CPP EGREP LIBOBJS LTLIBOBJS' +ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA PACKAGE VERSION ACLOCAL AUTOCONF AUTOMAKE AUTOHEADER MAKEINFO AMTAR install_sh STRIP ac_ct_STRIP INSTALL_STRIP_PROGRAM AWK SET_MAKE CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT DEPDIR am__include am__quote AMDEP_TRUE AMDEP_FALSE AMDEPBACKSLASH CCDEPMODE RANLIB ac_ct_RANLIB CPP EGREP ALLOCA LIBOBJS LIBS_ONLY_TRUE LIBS_ONLY_FALSE LTLIBOBJS' ac_subst_files='' # Initialize some variables set by options. @@ -2854,8 +2854,6 @@ else fi -# Checks for libraries. - # Checks for header files. @@ -3754,7 +3752,17 @@ done -for ac_header in fcntl.h limits.h mach/mach.h stdlib.h string.h syslog.h unistd.h + + + + + + + + + + +for ac_header in fcntl.h limits.h mach/mach.h netdb.h netinet/in.h paths.h stddef.h stdlib.h string.h sys/ioctl.h sys/mount.h sys/param.h sys/socket.h sys/time.h syslog.h termios.h unistd.h do as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` if eval "test \"\${$as_ac_Header+set}\" = set"; then @@ -4170,6 +4178,44 @@ _ACEOF fi +echo "$as_me:$LINENO: checking for uid_t in sys/types.h" >&5 +echo $ECHO_N "checking for uid_t in sys/types.h... $ECHO_C" >&6 +if test "${ac_cv_type_uid_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "uid_t" >/dev/null 2>&1; then + ac_cv_type_uid_t=yes +else + ac_cv_type_uid_t=no +fi +rm -f conftest* + +fi +echo "$as_me:$LINENO: result: $ac_cv_type_uid_t" >&5 +echo "${ECHO_T}$ac_cv_type_uid_t" >&6 +if test $ac_cv_type_uid_t = no; then + +cat >>confdefs.h <<\_ACEOF +#define uid_t int +_ACEOF + + +cat >>confdefs.h <<\_ACEOF +#define gid_t int +_ACEOF + +fi + echo "$as_me:$LINENO: checking for mode_t" >&5 echo $ECHO_N "checking for mode_t... $ECHO_C" >&6 if test "${ac_cv_type_mode_t+set}" = set; then @@ -4434,9 +4480,9 @@ _ACEOF fi -echo "$as_me:$LINENO: checking for uid_t in sys/types.h" >&5 -echo $ECHO_N "checking for uid_t in sys/types.h... $ECHO_C" >&6 -if test "${ac_cv_type_uid_t+set}" = set; then +echo "$as_me:$LINENO: checking whether time.h and sys/time.h may both be included" >&5 +echo $ECHO_N "checking whether time.h and sys/time.h may both be included... $ECHO_C" >&6 +if test "${ac_cv_header_time+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF @@ -4446,41 +4492,63 @@ cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include +#include +#include +int +main () +{ +if ((struct tm *) 0) +return 0; + ; + return 0; +} _ACEOF -if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | - $EGREP "uid_t" >/dev/null 2>&1; then - ac_cv_type_uid_t=yes +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_header_time=yes else - ac_cv_type_uid_t=no -fi -rm -f conftest* + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 +ac_cv_header_time=no fi -echo "$as_me:$LINENO: result: $ac_cv_type_uid_t" >&5 -echo "${ECHO_T}$ac_cv_type_uid_t" >&6 -if test $ac_cv_type_uid_t = no; then - -cat >>confdefs.h <<\_ACEOF -#define uid_t int -_ACEOF - +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_header_time" >&5 +echo "${ECHO_T}$ac_cv_header_time" >&6 +if test $ac_cv_header_time = yes; then cat >>confdefs.h <<\_ACEOF -#define gid_t int +#define TIME_WITH_SYS_TIME 1 _ACEOF fi - -# Checks for library functions. -echo "$as_me:$LINENO: checking whether closedir returns void" >&5 -echo $ECHO_N "checking whether closedir returns void... $ECHO_C" >&6 -if test "${ac_cv_func_closedir_void+set}" = set; then +echo "$as_me:$LINENO: checking whether struct tm is in sys/time.h or time.h" >&5 +echo $ECHO_N "checking whether struct tm is in sys/time.h or time.h... $ECHO_C" >&6 +if test "${ac_cv_struct_tm+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test "$cross_compiling" = yes; then - ac_cv_func_closedir_void=yes else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ @@ -4488,56 +4556,65 @@ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ -$ac_includes_default -#include <$ac_header_dirent> -#ifndef __cplusplus -int closedir (); -#endif +#include +#include int main () { -exit (closedir (opendir (".")) != 0); +struct tm *tp; tp->tm_sec; ; return 0; } _ACEOF -rm -f conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>&5 +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then - ac_cv_func_closedir_void=no + ac_cv_struct_tm=time.h else - echo "$as_me: program exited with status $ac_status" >&5 -echo "$as_me: failed program was:" >&5 + echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 -( exit $ac_status ) -ac_cv_func_closedir_void=yes -fi -rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +ac_cv_struct_tm=sys/time.h fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi -echo "$as_me:$LINENO: result: $ac_cv_func_closedir_void" >&5 -echo "${ECHO_T}$ac_cv_func_closedir_void" >&6 -if test $ac_cv_func_closedir_void = yes; then +echo "$as_me:$LINENO: result: $ac_cv_struct_tm" >&5 +echo "${ECHO_T}$ac_cv_struct_tm" >&6 +if test $ac_cv_struct_tm = sys/time.h; then cat >>confdefs.h <<\_ACEOF -#define CLOSEDIR_VOID 1 +#define TM_IN_SYS_TIME 1 _ACEOF fi -echo "$as_me:$LINENO: checking for error_at_line" >&5 -echo $ECHO_N "checking for error_at_line... $ECHO_C" >&6 -if test "${ac_cv_lib_error_at_line+set}" = set; then + +# Checks for library functions. +# The Ultrix 4.2 mips builtin alloca declared by alloca.h only works +# for constant arguments. Useless! +echo "$as_me:$LINENO: checking for working alloca.h" >&5 +echo $ECHO_N "checking for working alloca.h... $ECHO_C" >&6 +if test "${ac_cv_working_alloca_h+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF @@ -4546,11 +4623,11 @@ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ -$ac_includes_default +#include int main () { -error_at_line (0, 0, "", 0, ""); +char *p = (char *) alloca (2 * sizeof (int)); ; return 0; } @@ -4577,58 +4654,69 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then - ac_cv_lib_error_at_line=yes + ac_cv_working_alloca_h=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 -ac_cv_lib_error_at_line=no +ac_cv_working_alloca_h=no fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi -echo "$as_me:$LINENO: result: $ac_cv_lib_error_at_line" >&5 -echo "${ECHO_T}$ac_cv_lib_error_at_line" >&6 -if test $ac_cv_lib_error_at_line = no; then - case $LIBOBJS in - "error.$ac_objext" | \ - *" error.$ac_objext" | \ - "error.$ac_objext "* | \ - *" error.$ac_objext "* ) ;; - *) LIBOBJS="$LIBOBJS error.$ac_objext" ;; -esac - -fi +echo "$as_me:$LINENO: result: $ac_cv_working_alloca_h" >&5 +echo "${ECHO_T}$ac_cv_working_alloca_h" >&6 +if test $ac_cv_working_alloca_h = yes; then +cat >>confdefs.h <<\_ACEOF +#define HAVE_ALLOCA_H 1 +_ACEOF +fi -for ac_header in unistd.h vfork.h -do -as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` -if eval "test \"\${$as_ac_Header+set}\" = set"; then - echo "$as_me:$LINENO: checking for $ac_header" >&5 -echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 -if eval "test \"\${$as_ac_Header+set}\" = set"; then +echo "$as_me:$LINENO: checking for alloca" >&5 +echo $ECHO_N "checking for alloca... $ECHO_C" >&6 +if test "${ac_cv_func_alloca_works+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 -fi -echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 -echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 else - # Is the header compilable? -echo "$as_me:$LINENO: checking $ac_header usability" >&5 -echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 -cat >conftest.$ac_ext <<_ACEOF + cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ -$ac_includes_default -#include <$ac_header> +#ifdef __GNUC__ +# define alloca __builtin_alloca +#else +# ifdef _MSC_VER +# include +# define alloca _alloca +# else +# if HAVE_ALLOCA_H +# include +# else +# ifdef _AIX + #pragma alloca +# else +# ifndef alloca /* predefined by HP cc +Olibcalls */ +char *alloca (); +# endif +# endif +# endif +# endif +#endif + +int +main () +{ +char *p = (char *) alloca (1); + ; + return 0; +} _ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 @@ -4642,120 +4730,76 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' + { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then - ac_header_compiler=yes + ac_cv_func_alloca_works=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 -ac_header_compiler=no +ac_cv_func_alloca_works=no fi -rm -f conftest.err conftest.$ac_objext conftest.$ac_ext -echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 -echo "${ECHO_T}$ac_header_compiler" >&6 +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_func_alloca_works" >&5 +echo "${ECHO_T}$ac_cv_func_alloca_works" >&6 -# Is the header present? -echo "$as_me:$LINENO: checking $ac_header presence" >&5 -echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 -cat >conftest.$ac_ext <<_ACEOF +if test $ac_cv_func_alloca_works = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_ALLOCA 1 +_ACEOF + +else + # The SVR3 libPW and SVR4 libucb both contain incompatible functions +# that cause trouble. Some versions do not even contain alloca or +# contain a buggy version. If you still want to use their alloca, +# use ar to extract alloca.o from them instead of compiling alloca.c. + +ALLOCA=alloca.$ac_objext + +cat >>confdefs.h <<\_ACEOF +#define C_ALLOCA 1 +_ACEOF + + +echo "$as_me:$LINENO: checking whether \`alloca.c' needs Cray hooks" >&5 +echo $ECHO_N "checking whether \`alloca.c' needs Cray hooks... $ECHO_C" >&6 +if test "${ac_cv_os_cray+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ -#include <$ac_header> +#if defined(CRAY) && ! defined(CRAY2) +webecray +#else +wenotbecray +#endif + _ACEOF -if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 - (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } >/dev/null; then - if test -s conftest.err; then - ac_cpp_err=$ac_c_preproc_warn_flag - ac_cpp_err=$ac_cpp_err$ac_c_werror_flag - else - ac_cpp_err= - fi +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "webecray" >/dev/null 2>&1; then + ac_cv_os_cray=yes else - ac_cpp_err=yes + ac_cv_os_cray=no fi -if test -z "$ac_cpp_err"; then - ac_header_preproc=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 +rm -f conftest* - ac_header_preproc=no fi -rm -f conftest.err conftest.$ac_ext -echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 -echo "${ECHO_T}$ac_header_preproc" >&6 - -# So? What about this header? -case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in - yes:no: ) - { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 -echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 -echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} - ac_header_preproc=yes - ;; - no:yes:* ) - { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 -echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 -echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 -echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 -echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 -echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 -echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} - ( - cat <<\_ASBOX -## -------------------------------------------------- ## -## Report this to launchd-bug-reports@group.apple.com ## -## -------------------------------------------------- ## -_ASBOX - ) | - sed "s/^/$as_me: WARNING: /" >&2 - ;; -esac -echo "$as_me:$LINENO: checking for $ac_header" >&5 -echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 -if eval "test \"\${$as_ac_Header+set}\" = set"; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - eval "$as_ac_Header=\$ac_header_preproc" -fi -echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 -echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 - -fi -if test `eval echo '${'$as_ac_Header'}'` = yes; then - cat >>confdefs.h <<_ACEOF -#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 -_ACEOF - -fi - -done - - - -for ac_func in fork vfork -do -as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: result: $ac_cv_os_cray" >&5 +echo "${ECHO_T}$ac_cv_os_cray" >&6 +if test $ac_cv_os_cray = yes; then + for ac_func in _getb67 GETB67 getb67; do + as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` echo "$as_me:$LINENO: checking for $ac_func" >&5 echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 if eval "test \"\${$as_ac_var+set}\" = set"; then @@ -4847,35 +4891,50 @@ fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 if test `eval echo '${'$as_ac_var'}'` = yes; then - cat >>confdefs.h <<_ACEOF -#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 + +cat >>confdefs.h <<_ACEOF +#define CRAY_STACKSEG_END $ac_func _ACEOF + break +fi + + done fi -done -if test "x$ac_cv_func_fork" = xyes; then - echo "$as_me:$LINENO: checking for working fork" >&5 -echo $ECHO_N "checking for working fork... $ECHO_C" >&6 -if test "${ac_cv_func_fork_works+set}" = set; then +echo "$as_me:$LINENO: checking stack direction for C alloca" >&5 +echo $ECHO_N "checking stack direction for C alloca... $ECHO_C" >&6 +if test "${ac_cv_c_stack_direction+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test "$cross_compiling" = yes; then - ac_cv_func_fork_works=cross + ac_cv_c_stack_direction=0 else cat >conftest.$ac_ext <<_ACEOF -/* By Ruediger Kuhlmann. */ - #include - #if HAVE_UNISTD_H - # include - #endif - /* Some systems only have a dummy stub for fork() */ - int main () - { - if (fork() < 0) - exit (1); - exit (0); - } +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +int +find_stack_direction () +{ + static char *addr = 0; + auto char dummy; + if (addr == 0) + { + addr = &dummy; + return find_stack_direction (); + } + else + return (&dummy > addr) ? 1 : -1; +} + +int +main () +{ + exit (find_stack_direction () < 0); +} _ACEOF rm -f conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 @@ -4888,46 +4947,35 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then - ac_cv_func_fork_works=yes + ac_cv_c_stack_direction=1 else echo "$as_me: program exited with status $ac_status" >&5 echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ( exit $ac_status ) -ac_cv_func_fork_works=no +ac_cv_c_stack_direction=-1 fi rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext fi fi -echo "$as_me:$LINENO: result: $ac_cv_func_fork_works" >&5 -echo "${ECHO_T}$ac_cv_func_fork_works" >&6 +echo "$as_me:$LINENO: result: $ac_cv_c_stack_direction" >&5 +echo "${ECHO_T}$ac_cv_c_stack_direction" >&6 + +cat >>confdefs.h <<_ACEOF +#define STACK_DIRECTION $ac_cv_c_stack_direction +_ACEOF + -else - ac_cv_func_fork_works=$ac_cv_func_fork -fi -if test "x$ac_cv_func_fork_works" = xcross; then - case $host in - *-*-amigaos* | *-*-msdosdjgpp*) - # Override, as these systems have only a dummy fork() stub - ac_cv_func_fork_works=no - ;; - *) - ac_cv_func_fork_works=yes - ;; - esac - { echo "$as_me:$LINENO: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&5 -echo "$as_me: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&2;} fi -ac_cv_func_vfork_works=$ac_cv_func_vfork -if test "x$ac_cv_func_vfork" = xyes; then - echo "$as_me:$LINENO: checking for working vfork" >&5 -echo $ECHO_N "checking for working vfork... $ECHO_C" >&6 -if test "${ac_cv_func_vfork_works+set}" = set; then + +echo "$as_me:$LINENO: checking whether closedir returns void" >&5 +echo $ECHO_N "checking whether closedir returns void... $ECHO_C" >&6 +if test "${ac_cv_func_closedir_void+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test "$cross_compiling" = yes; then - ac_cv_func_vfork_works=cross + ac_cv_func_closedir_void=yes else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ @@ -4935,100 +4983,18 @@ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ -/* Thanks to Paul Eggert for this test. */ -#include -#include -#include -#include -#include -#if HAVE_UNISTD_H -# include -#endif -#if HAVE_VFORK_H -# include -#endif -/* On some sparc systems, changes by the child to local and incoming - argument registers are propagated back to the parent. The compiler - is told about this with #include , but some compilers - (e.g. gcc -O) don't grok . Test for this by using a - static variable whose address is put into a register that is - clobbered by the vfork. */ -static void -#ifdef __cplusplus -sparc_address_test (int arg) -# else -sparc_address_test (arg) int arg; +$ac_includes_default +#include <$ac_header_dirent> +#ifndef __cplusplus +int closedir (); #endif -{ - static pid_t child; - if (!child) { - child = vfork (); - if (child < 0) { - perror ("vfork"); - _exit(2); - } - if (!child) { - arg = getpid(); - write(-1, "", 0); - _exit (arg); - } - } -} int main () { - pid_t parent = getpid (); - pid_t child; - - sparc_address_test (0); - - child = vfork (); - - if (child == 0) { - /* Here is another test for sparc vfork register problems. This - test uses lots of local variables, at least as many local - variables as main has allocated so far including compiler - temporaries. 4 locals are enough for gcc 1.40.3 on a Solaris - 4.1.3 sparc, but we use 8 to be safe. A buggy compiler should - reuse the register of parent for one of the local variables, - since it will think that parent can't possibly be used any more - in this routine. Assigning to the local variable will thus - munge parent in the parent process. */ - pid_t - p = getpid(), p1 = getpid(), p2 = getpid(), p3 = getpid(), - p4 = getpid(), p5 = getpid(), p6 = getpid(), p7 = getpid(); - /* Convince the compiler that p..p7 are live; otherwise, it might - use the same hardware register for all 8 local variables. */ - if (p != p1 || p != p2 || p != p3 || p != p4 - || p != p5 || p != p6 || p != p7) - _exit(1); - - /* On some systems (e.g. IRIX 3.3), vfork doesn't separate parent - from child file descriptors. If the child closes a descriptor - before it execs or exits, this munges the parent's descriptor - as well. Test for this by closing stdout in the child. */ - _exit(close(fileno(stdout)) != 0); - } else { - int status; - struct stat st; - - while (wait(&status) != child) - ; - exit( - /* Was there some problem with vforking? */ - child < 0 - - /* Did the child fail? (This shouldn't happen.) */ - || status - - /* Did the vfork/compiler bug occur? */ - || parent != getpid() - - /* Did the file descriptor bug occur? */ - || fstat(fileno(stdout), &st) != 0 - ); - } +exit (closedir (opendir (".")) != 0); + ; + return 0; } _ACEOF rm -f conftest$ac_exeext @@ -5042,51 +5008,31 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then - ac_cv_func_vfork_works=yes + ac_cv_func_closedir_void=no else echo "$as_me: program exited with status $ac_status" >&5 echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ( exit $ac_status ) -ac_cv_func_vfork_works=no +ac_cv_func_closedir_void=yes fi rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext fi fi -echo "$as_me:$LINENO: result: $ac_cv_func_vfork_works" >&5 -echo "${ECHO_T}$ac_cv_func_vfork_works" >&6 - -fi; -if test "x$ac_cv_func_fork_works" = xcross; then - ac_cv_func_vfork_works=$ac_cv_func_vfork - { echo "$as_me:$LINENO: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&5 -echo "$as_me: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&2;} -fi - -if test "x$ac_cv_func_vfork_works" = xyes; then - -cat >>confdefs.h <<\_ACEOF -#define HAVE_WORKING_VFORK 1 -_ACEOF - -else +echo "$as_me:$LINENO: result: $ac_cv_func_closedir_void" >&5 +echo "${ECHO_T}$ac_cv_func_closedir_void" >&6 +if test $ac_cv_func_closedir_void = yes; then cat >>confdefs.h <<\_ACEOF -#define vfork fork +#define CLOSEDIR_VOID 1 _ACEOF fi -if test "x$ac_cv_func_fork_works" = xyes; then - -cat >>confdefs.h <<\_ACEOF -#define HAVE_WORKING_FORK 1 -_ACEOF -fi -for ac_header in stdlib.h +for ac_header in unistd.h vfork.h do as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` if eval "test \"\${$as_ac_Header+set}\" = set"; then @@ -5235,13 +5181,15 @@ fi done -echo "$as_me:$LINENO: checking for GNU libc compatible malloc" >&5 -echo $ECHO_N "checking for GNU libc compatible malloc... $ECHO_C" >&6 -if test "${ac_cv_func_malloc_0_nonnull+set}" = set; then + + +for ac_func in fork vfork +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test "$cross_compiling" = yes; then - ac_cv_func_malloc_0_nonnull=no else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ @@ -5249,20 +5197,116 @@ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ -#if STDC_HEADERS || HAVE_STDLIB_H -# include +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include #else -char *malloc (); +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} #endif int main () { -exit (malloc (0) ? 0 : 1); +return f != $ac_func; ; return 0; } _ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + +if test "x$ac_cv_func_fork" = xyes; then + echo "$as_me:$LINENO: checking for working fork" >&5 +echo $ECHO_N "checking for working fork... $ECHO_C" >&6 +if test "${ac_cv_func_fork_works+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_fork_works=cross +else + cat >conftest.$ac_ext <<_ACEOF +/* By Ruediger Kuhlmann. */ + #include + #if HAVE_UNISTD_H + # include + #endif + /* Some systems only have a dummy stub for fork() */ + int main () + { + if (fork() < 0) + exit (1); + exit (0); + } +_ACEOF rm -f conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>&5 @@ -5274,51 +5318,1606 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then - ac_cv_func_malloc_0_nonnull=yes + ac_cv_func_fork_works=yes else echo "$as_me: program exited with status $ac_status" >&5 echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ( exit $ac_status ) -ac_cv_func_malloc_0_nonnull=no +ac_cv_func_fork_works=no fi rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext fi fi -echo "$as_me:$LINENO: result: $ac_cv_func_malloc_0_nonnull" >&5 -echo "${ECHO_T}$ac_cv_func_malloc_0_nonnull" >&6 -if test $ac_cv_func_malloc_0_nonnull = yes; then - -cat >>confdefs.h <<\_ACEOF -#define HAVE_MALLOC 1 -_ACEOF +echo "$as_me:$LINENO: result: $ac_cv_func_fork_works" >&5 +echo "${ECHO_T}$ac_cv_func_fork_works" >&6 else - cat >>confdefs.h <<\_ACEOF -#define HAVE_MALLOC 0 + ac_cv_func_fork_works=$ac_cv_func_fork +fi +if test "x$ac_cv_func_fork_works" = xcross; then + case $host in + *-*-amigaos* | *-*-msdosdjgpp*) + # Override, as these systems have only a dummy fork() stub + ac_cv_func_fork_works=no + ;; + *) + ac_cv_func_fork_works=yes + ;; + esac + { echo "$as_me:$LINENO: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&5 +echo "$as_me: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&2;} +fi +ac_cv_func_vfork_works=$ac_cv_func_vfork +if test "x$ac_cv_func_vfork" = xyes; then + echo "$as_me:$LINENO: checking for working vfork" >&5 +echo $ECHO_N "checking for working vfork... $ECHO_C" >&6 +if test "${ac_cv_func_vfork_works+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_vfork_works=cross +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ _ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Thanks to Paul Eggert for this test. */ +#include +#include +#include +#include +#include +#if HAVE_UNISTD_H +# include +#endif +#if HAVE_VFORK_H +# include +#endif +/* On some sparc systems, changes by the child to local and incoming + argument registers are propagated back to the parent. The compiler + is told about this with #include , but some compilers + (e.g. gcc -O) don't grok . Test for this by using a + static variable whose address is put into a register that is + clobbered by the vfork. */ +static void +#ifdef __cplusplus +sparc_address_test (int arg) +# else +sparc_address_test (arg) int arg; +#endif +{ + static pid_t child; + if (!child) { + child = vfork (); + if (child < 0) { + perror ("vfork"); + _exit(2); + } + if (!child) { + arg = getpid(); + write(-1, "", 0); + _exit (arg); + } + } +} - case $LIBOBJS in - "malloc.$ac_objext" | \ - *" malloc.$ac_objext" | \ - "malloc.$ac_objext "* | \ - *" malloc.$ac_objext "* ) ;; - *) LIBOBJS="$LIBOBJS malloc.$ac_objext" ;; -esac +int +main () +{ + pid_t parent = getpid (); + pid_t child; + sparc_address_test (0); -cat >>confdefs.h <<\_ACEOF -#define malloc rpl_malloc -_ACEOF + child = vfork (); -fi + if (child == 0) { + /* Here is another test for sparc vfork register problems. This + test uses lots of local variables, at least as many local + variables as main has allocated so far including compiler + temporaries. 4 locals are enough for gcc 1.40.3 on a Solaris + 4.1.3 sparc, but we use 8 to be safe. A buggy compiler should + reuse the register of parent for one of the local variables, + since it will think that parent can't possibly be used any more + in this routine. Assigning to the local variable will thus + munge parent in the parent process. */ + pid_t + p = getpid(), p1 = getpid(), p2 = getpid(), p3 = getpid(), + p4 = getpid(), p5 = getpid(), p6 = getpid(), p7 = getpid(); + /* Convince the compiler that p..p7 are live; otherwise, it might + use the same hardware register for all 8 local variables. */ + if (p != p1 || p != p2 || p != p3 || p != p4 + || p != p5 || p != p6 || p != p7) + _exit(1); + + /* On some systems (e.g. IRIX 3.3), vfork doesn't separate parent + from child file descriptors. If the child closes a descriptor + before it execs or exits, this munges the parent's descriptor + as well. Test for this by closing stdout in the child. */ + _exit(close(fileno(stdout)) != 0); + } else { + int status; + struct stat st; + + while (wait(&status) != child) + ; + exit( + /* Was there some problem with vforking? */ + child < 0 + /* Did the child fail? (This shouldn't happen.) */ + || status + /* Did the vfork/compiler bug occur? */ + || parent != getpid() + + /* Did the file descriptor bug occur? */ + || fstat(fileno(stdout), &st) != 0 + ); + } +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_vfork_works=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_vfork_works=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_func_vfork_works" >&5 +echo "${ECHO_T}$ac_cv_func_vfork_works" >&6 + +fi; +if test "x$ac_cv_func_fork_works" = xcross; then + ac_cv_func_vfork_works=$ac_cv_func_vfork + { echo "$as_me:$LINENO: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&5 +echo "$as_me: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&2;} +fi + +if test "x$ac_cv_func_vfork_works" = xyes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_WORKING_VFORK 1 +_ACEOF + +else + +cat >>confdefs.h <<\_ACEOF +#define vfork fork +_ACEOF + +fi +if test "x$ac_cv_func_fork_works" = xyes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_WORKING_FORK 1 +_ACEOF + +fi + +if test $ac_cv_c_compiler_gnu = yes; then + echo "$as_me:$LINENO: checking whether $CC needs -traditional" >&5 +echo $ECHO_N "checking whether $CC needs -traditional... $ECHO_C" >&6 +if test "${ac_cv_prog_gcc_traditional+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_pattern="Autoconf.*'x'" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +Autoconf TIOCGETP +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "$ac_pattern" >/dev/null 2>&1; then + ac_cv_prog_gcc_traditional=yes +else + ac_cv_prog_gcc_traditional=no +fi +rm -f conftest* + + + if test $ac_cv_prog_gcc_traditional = no; then + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +Autoconf TCGETA +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "$ac_pattern" >/dev/null 2>&1; then + ac_cv_prog_gcc_traditional=yes +fi +rm -f conftest* + + fi +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_gcc_traditional" >&5 +echo "${ECHO_T}$ac_cv_prog_gcc_traditional" >&6 + if test $ac_cv_prog_gcc_traditional = yes; then + CC="$CC -traditional" + fi +fi + +echo "$as_me:$LINENO: checking whether lstat dereferences a symlink specified with a trailing slash" >&5 +echo $ECHO_N "checking whether lstat dereferences a symlink specified with a trailing slash... $ECHO_C" >&6 +if test "${ac_cv_func_lstat_dereferences_slashed_symlink+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + rm -f conftest.sym conftest.file +echo >conftest.file +if test "$as_ln_s" = "ln -s" && ln -s conftest.file conftest.sym; then + if test "$cross_compiling" = yes; then + ac_cv_func_lstat_dereferences_slashed_symlink=no +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +struct stat sbuf; + /* Linux will dereference the symlink and fail. + That is better in the sense that it means we will not + have to compile and use the lstat wrapper. */ + exit (lstat ("conftest.sym/", &sbuf) ? 0 : 1); + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_lstat_dereferences_slashed_symlink=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_lstat_dereferences_slashed_symlink=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +else + # If the `ln -s' command failed, then we probably don't even + # have an lstat function. + ac_cv_func_lstat_dereferences_slashed_symlink=no +fi +rm -f conftest.sym conftest.file + +fi +echo "$as_me:$LINENO: result: $ac_cv_func_lstat_dereferences_slashed_symlink" >&5 +echo "${ECHO_T}$ac_cv_func_lstat_dereferences_slashed_symlink" >&6 + +test $ac_cv_func_lstat_dereferences_slashed_symlink = yes && + +cat >>confdefs.h <<_ACEOF +#define LSTAT_FOLLOWS_SLASHED_SYMLINK 1 +_ACEOF + + +if test $ac_cv_func_lstat_dereferences_slashed_symlink = no; then + case $LIBOBJS in + "lstat.$ac_objext" | \ + *" lstat.$ac_objext" | \ + "lstat.$ac_objext "* | \ + *" lstat.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS lstat.$ac_objext" ;; +esac + +fi + +echo "$as_me:$LINENO: checking whether lstat accepts an empty string" >&5 +echo $ECHO_N "checking whether lstat accepts an empty string... $ECHO_C" >&6 +if test "${ac_cv_func_lstat_empty_string_bug+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_lstat_empty_string_bug=yes +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +struct stat sbuf; + exit (lstat ("", &sbuf) ? 1 : 0); + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_lstat_empty_string_bug=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_lstat_empty_string_bug=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_func_lstat_empty_string_bug" >&5 +echo "${ECHO_T}$ac_cv_func_lstat_empty_string_bug" >&6 +if test $ac_cv_func_lstat_empty_string_bug = yes; then + case $LIBOBJS in + "lstat.$ac_objext" | \ + *" lstat.$ac_objext" | \ + "lstat.$ac_objext "* | \ + *" lstat.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS lstat.$ac_objext" ;; +esac + + +cat >>confdefs.h <<_ACEOF +#define HAVE_LSTAT_EMPTY_STRING_BUG 1 +_ACEOF + +fi + +echo "$as_me:$LINENO: checking whether lstat dereferences a symlink specified with a trailing slash" >&5 +echo $ECHO_N "checking whether lstat dereferences a symlink specified with a trailing slash... $ECHO_C" >&6 +if test "${ac_cv_func_lstat_dereferences_slashed_symlink+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + rm -f conftest.sym conftest.file +echo >conftest.file +if test "$as_ln_s" = "ln -s" && ln -s conftest.file conftest.sym; then + if test "$cross_compiling" = yes; then + ac_cv_func_lstat_dereferences_slashed_symlink=no +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +struct stat sbuf; + /* Linux will dereference the symlink and fail. + That is better in the sense that it means we will not + have to compile and use the lstat wrapper. */ + exit (lstat ("conftest.sym/", &sbuf) ? 0 : 1); + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_lstat_dereferences_slashed_symlink=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_lstat_dereferences_slashed_symlink=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +else + # If the `ln -s' command failed, then we probably don't even + # have an lstat function. + ac_cv_func_lstat_dereferences_slashed_symlink=no +fi +rm -f conftest.sym conftest.file + +fi +echo "$as_me:$LINENO: result: $ac_cv_func_lstat_dereferences_slashed_symlink" >&5 +echo "${ECHO_T}$ac_cv_func_lstat_dereferences_slashed_symlink" >&6 + +test $ac_cv_func_lstat_dereferences_slashed_symlink = yes && + +cat >>confdefs.h <<_ACEOF +#define LSTAT_FOLLOWS_SLASHED_SYMLINK 1 +_ACEOF + + +if test $ac_cv_func_lstat_dereferences_slashed_symlink = no; then + case $LIBOBJS in + "lstat.$ac_objext" | \ + *" lstat.$ac_objext" | \ + "lstat.$ac_objext "* | \ + *" lstat.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS lstat.$ac_objext" ;; +esac + +fi + + +for ac_header in stdlib.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## -------------------------------------------------- ## +## Report this to launchd-bug-reports@group.apple.com ## +## -------------------------------------------------- ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + +echo "$as_me:$LINENO: checking for GNU libc compatible malloc" >&5 +echo $ECHO_N "checking for GNU libc compatible malloc... $ECHO_C" >&6 +if test "${ac_cv_func_malloc_0_nonnull+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_malloc_0_nonnull=no +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#if STDC_HEADERS || HAVE_STDLIB_H +# include +#else +char *malloc (); +#endif + +int +main () +{ +exit (malloc (0) ? 0 : 1); + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_malloc_0_nonnull=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_malloc_0_nonnull=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_func_malloc_0_nonnull" >&5 +echo "${ECHO_T}$ac_cv_func_malloc_0_nonnull" >&6 +if test $ac_cv_func_malloc_0_nonnull = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_MALLOC 1 +_ACEOF + +else + cat >>confdefs.h <<\_ACEOF +#define HAVE_MALLOC 0 +_ACEOF + + case $LIBOBJS in + "malloc.$ac_objext" | \ + *" malloc.$ac_objext" | \ + "malloc.$ac_objext "* | \ + *" malloc.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS malloc.$ac_objext" ;; +esac + + +cat >>confdefs.h <<\_ACEOF +#define malloc rpl_malloc +_ACEOF + +fi + + + + + + +for ac_header in stdlib.h sys/time.h unistd.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## -------------------------------------------------- ## +## Report this to launchd-bug-reports@group.apple.com ## +## -------------------------------------------------- ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +for ac_func in alarm +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + +echo "$as_me:$LINENO: checking for working mktime" >&5 +echo $ECHO_N "checking for working mktime... $ECHO_C" >&6 +if test "${ac_cv_func_working_mktime+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_working_mktime=no +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Test program from Paul Eggert and Tony Leneis. */ +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif + +#if HAVE_STDLIB_H +# include +#endif + +#if HAVE_UNISTD_H +# include +#endif + +#if !HAVE_ALARM +# define alarm(X) /* empty */ +#endif + +/* Work around redefinition to rpl_putenv by other config tests. */ +#undef putenv + +static time_t time_t_max; +static time_t time_t_min; + +/* Values we'll use to set the TZ environment variable. */ +static char *tz_strings[] = { + (char *) 0, "TZ=GMT0", "TZ=JST-9", + "TZ=EST+3EDT+2,M10.1.0/00:00:00,M2.3.0/00:00:00" +}; +#define N_STRINGS (sizeof (tz_strings) / sizeof (tz_strings[0])) + +/* Fail if mktime fails to convert a date in the spring-forward gap. + Based on a problem report from Andreas Jaeger. */ +static void +spring_forward_gap () +{ + /* glibc (up to about 1998-10-07) failed this test. */ + struct tm tm; + + /* Use the portable POSIX.1 specification "TZ=PST8PDT,M4.1.0,M10.5.0" + instead of "TZ=America/Vancouver" in order to detect the bug even + on systems that don't support the Olson extension, or don't have the + full zoneinfo tables installed. */ + putenv ("TZ=PST8PDT,M4.1.0,M10.5.0"); + + tm.tm_year = 98; + tm.tm_mon = 3; + tm.tm_mday = 5; + tm.tm_hour = 2; + tm.tm_min = 0; + tm.tm_sec = 0; + tm.tm_isdst = -1; + if (mktime (&tm) == (time_t)-1) + exit (1); +} + +static void +mktime_test1 (now) + time_t now; +{ + struct tm *lt; + if ((lt = localtime (&now)) && mktime (lt) != now) + exit (1); +} + +static void +mktime_test (now) + time_t now; +{ + mktime_test1 (now); + mktime_test1 ((time_t) (time_t_max - now)); + mktime_test1 ((time_t) (time_t_min + now)); +} + +static void +irix_6_4_bug () +{ + /* Based on code from Ariel Faigon. */ + struct tm tm; + tm.tm_year = 96; + tm.tm_mon = 3; + tm.tm_mday = 0; + tm.tm_hour = 0; + tm.tm_min = 0; + tm.tm_sec = 0; + tm.tm_isdst = -1; + mktime (&tm); + if (tm.tm_mon != 2 || tm.tm_mday != 31) + exit (1); +} + +static void +bigtime_test (j) + int j; +{ + struct tm tm; + time_t now; + tm.tm_year = tm.tm_mon = tm.tm_mday = tm.tm_hour = tm.tm_min = tm.tm_sec = j; + now = mktime (&tm); + if (now != (time_t) -1) + { + struct tm *lt = localtime (&now); + if (! (lt + && lt->tm_year == tm.tm_year + && lt->tm_mon == tm.tm_mon + && lt->tm_mday == tm.tm_mday + && lt->tm_hour == tm.tm_hour + && lt->tm_min == tm.tm_min + && lt->tm_sec == tm.tm_sec + && lt->tm_yday == tm.tm_yday + && lt->tm_wday == tm.tm_wday + && ((lt->tm_isdst < 0 ? -1 : 0 < lt->tm_isdst) + == (tm.tm_isdst < 0 ? -1 : 0 < tm.tm_isdst)))) + exit (1); + } +} + +int +main () +{ + time_t t, delta; + int i, j; + + /* This test makes some buggy mktime implementations loop. + Give up after 60 seconds; a mktime slower than that + isn't worth using anyway. */ + alarm (60); + + for (time_t_max = 1; 0 < time_t_max; time_t_max *= 2) + continue; + time_t_max--; + if ((time_t) -1 < 0) + for (time_t_min = -1; (time_t) (time_t_min * 2) < 0; time_t_min *= 2) + continue; + delta = time_t_max / 997; /* a suitable prime number */ + for (i = 0; i < N_STRINGS; i++) + { + if (tz_strings[i]) + putenv (tz_strings[i]); + + for (t = 0; t <= time_t_max - delta; t += delta) + mktime_test (t); + mktime_test ((time_t) 1); + mktime_test ((time_t) (60 * 60)); + mktime_test ((time_t) (60 * 60 * 24)); + + for (j = 1; 0 < j; j *= 2) + bigtime_test (j); + bigtime_test (j - 1); + } + irix_6_4_bug (); + spring_forward_gap (); + exit (0); +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_working_mktime=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_working_mktime=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_func_working_mktime" >&5 +echo "${ECHO_T}$ac_cv_func_working_mktime" >&6 +if test $ac_cv_func_working_mktime = no; then + case $LIBOBJS in + "mktime.$ac_objext" | \ + *" mktime.$ac_objext" | \ + "mktime.$ac_objext "* | \ + *" mktime.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS mktime.$ac_objext" ;; +esac + +fi + + + +for ac_header in stdlib.h unistd.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## -------------------------------------------------- ## +## Report this to launchd-bug-reports@group.apple.com ## +## -------------------------------------------------- ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +for ac_func in getpagesize +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + +echo "$as_me:$LINENO: checking for working mmap" >&5 +echo $ECHO_N "checking for working mmap... $ECHO_C" >&6 +if test "${ac_cv_func_mmap_fixed_mapped+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_mmap_fixed_mapped=no +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +/* malloc might have been renamed as rpl_malloc. */ +#undef malloc + +/* Thanks to Mike Haertel and Jim Avera for this test. + Here is a matrix of mmap possibilities: + mmap private not fixed + mmap private fixed at somewhere currently unmapped + mmap private fixed at somewhere already mapped + mmap shared not fixed + mmap shared fixed at somewhere currently unmapped + mmap shared fixed at somewhere already mapped + For private mappings, we should verify that changes cannot be read() + back from the file, nor mmap's back from the file at a different + address. (There have been systems where private was not correctly + implemented like the infamous i386 svr4.0, and systems where the + VM page cache was not coherent with the file system buffer cache + like early versions of FreeBSD and possibly contemporary NetBSD.) + For shared mappings, we should conversely verify that changes get + propagated back to all the places they're supposed to be. + + Grep wants private fixed already mapped. + The main things grep needs to know about mmap are: + * does it exist and is it safe to write into the mmap'd area + * how to use it (BSD variants) */ + +#include +#include + +#if !STDC_HEADERS && !HAVE_STDLIB_H +char *malloc (); +#endif + +/* This mess was copied from the GNU getpagesize.h. */ +#if !HAVE_GETPAGESIZE +/* Assume that all systems that can run configure have sys/param.h. */ +# if !HAVE_SYS_PARAM_H +# define HAVE_SYS_PARAM_H 1 +# endif + +# ifdef _SC_PAGESIZE +# define getpagesize() sysconf(_SC_PAGESIZE) +# else /* no _SC_PAGESIZE */ +# if HAVE_SYS_PARAM_H +# include +# ifdef EXEC_PAGESIZE +# define getpagesize() EXEC_PAGESIZE +# else /* no EXEC_PAGESIZE */ +# ifdef NBPG +# define getpagesize() NBPG * CLSIZE +# ifndef CLSIZE +# define CLSIZE 1 +# endif /* no CLSIZE */ +# else /* no NBPG */ +# ifdef NBPC +# define getpagesize() NBPC +# else /* no NBPC */ +# ifdef PAGESIZE +# define getpagesize() PAGESIZE +# endif /* PAGESIZE */ +# endif /* no NBPC */ +# endif /* no NBPG */ +# endif /* no EXEC_PAGESIZE */ +# else /* no HAVE_SYS_PARAM_H */ +# define getpagesize() 8192 /* punt totally */ +# endif /* no HAVE_SYS_PARAM_H */ +# endif /* no _SC_PAGESIZE */ + +#endif /* no HAVE_GETPAGESIZE */ + +int +main () +{ + char *data, *data2, *data3; + int i, pagesize; + int fd; + + pagesize = getpagesize (); + + /* First, make a file with some known garbage in it. */ + data = (char *) malloc (pagesize); + if (!data) + exit (1); + for (i = 0; i < pagesize; ++i) + *(data + i) = rand (); + umask (0); + fd = creat ("conftest.mmap", 0600); + if (fd < 0) + exit (1); + if (write (fd, data, pagesize) != pagesize) + exit (1); + close (fd); + + /* Next, try to mmap the file at a fixed address which already has + something else allocated at it. If we can, also make sure that + we see the same garbage. */ + fd = open ("conftest.mmap", O_RDWR); + if (fd < 0) + exit (1); + data2 = (char *) malloc (2 * pagesize); + if (!data2) + exit (1); + data2 += (pagesize - ((long) data2 & (pagesize - 1))) & (pagesize - 1); + if (data2 != mmap (data2, pagesize, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_FIXED, fd, 0L)) + exit (1); + for (i = 0; i < pagesize; ++i) + if (*(data + i) != *(data2 + i)) + exit (1); + + /* Finally, make sure that changes to the mapped area do not + percolate back to the file as seen by read(). (This is a bug on + some variants of i386 svr4.0.) */ + for (i = 0; i < pagesize; ++i) + *(data2 + i) = *(data2 + i) + 1; + data3 = (char *) malloc (pagesize); + if (!data3) + exit (1); + if (read (fd, data3, pagesize) != pagesize) + exit (1); + for (i = 0; i < pagesize; ++i) + if (*(data + i) != *(data3 + i)) + exit (1); + close (fd); + exit (0); +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_mmap_fixed_mapped=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_mmap_fixed_mapped=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_func_mmap_fixed_mapped" >&5 +echo "${ECHO_T}$ac_cv_func_mmap_fixed_mapped" >&6 +if test $ac_cv_func_mmap_fixed_mapped = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_MMAP 1 +_ACEOF +fi +rm -f conftest.mmap -for ac_header in stdlib.h unistd.h +for ac_header in stdlib.h do as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` if eval "test \"\${$as_ac_Header+set}\" = set"; then @@ -5467,14 +7066,13 @@ fi done - -for ac_func in getpagesize -do -as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` -echo "$as_me:$LINENO: checking for $ac_func" >&5 -echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 -if eval "test \"\${$as_ac_var+set}\" = set"; then +echo "$as_me:$LINENO: checking for GNU libc compatible realloc" >&5 +echo $ECHO_N "checking for GNU libc compatible realloc... $ECHO_C" >&6 +if test "${ac_cv_func_realloc_0_nonnull+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_realloc_0_nonnull=no else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ @@ -5482,294 +7080,252 @@ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ -/* Define $ac_func to an innocuous variant, in case declares $ac_func. - For example, HP-UX 11i declares gettimeofday. */ -#define $ac_func innocuous_$ac_func - -/* System header to define __stub macros and hopefully few prototypes, - which can conflict with char $ac_func (); below. - Prefer to if __STDC__ is defined, since - exists even on freestanding compilers. */ - -#ifdef __STDC__ -# include -#else -# include -#endif - -#undef $ac_func - -/* Override any gcc2 internal prototype to avoid an error. */ -#ifdef __cplusplus -extern "C" -{ -#endif -/* We use char because int might match the return type of a gcc2 - builtin and then its argument prototype would still apply. */ -char $ac_func (); -/* The GNU C library defines this for functions which it implements - to always fail with ENOSYS. Some functions are actually named - something starting with __ and the normal name is an alias. */ -#if defined (__stub_$ac_func) || defined (__stub___$ac_func) -choke me +#if STDC_HEADERS || HAVE_STDLIB_H +# include #else -char (*f) () = $ac_func; -#endif -#ifdef __cplusplus -} +char *realloc (); #endif int main () { -return f != $ac_func; +exit (realloc (0, 0) ? 0 : 1); ; return 0; } _ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext +rm -f conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 + (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest$ac_exeext' + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then - eval "$as_ac_var=yes" + ac_cv_func_realloc_0_nonnull=yes else - echo "$as_me: failed program was:" >&5 + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 -eval "$as_ac_var=no" +( exit $ac_status ) +ac_cv_func_realloc_0_nonnull=no fi -rm -f conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext fi -echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 -echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 -if test `eval echo '${'$as_ac_var'}'` = yes; then - cat >>confdefs.h <<_ACEOF -#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 -_ACEOF - fi -done +echo "$as_me:$LINENO: result: $ac_cv_func_realloc_0_nonnull" >&5 +echo "${ECHO_T}$ac_cv_func_realloc_0_nonnull" >&6 +if test $ac_cv_func_realloc_0_nonnull = yes; then -echo "$as_me:$LINENO: checking for working mmap" >&5 -echo $ECHO_N "checking for working mmap... $ECHO_C" >&6 -if test "${ac_cv_func_mmap_fixed_mapped+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test "$cross_compiling" = yes; then - ac_cv_func_mmap_fixed_mapped=no -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ +cat >>confdefs.h <<\_ACEOF +#define HAVE_REALLOC 1 _ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_includes_default -/* malloc might have been renamed as rpl_malloc. */ -#undef malloc - -/* Thanks to Mike Haertel and Jim Avera for this test. - Here is a matrix of mmap possibilities: - mmap private not fixed - mmap private fixed at somewhere currently unmapped - mmap private fixed at somewhere already mapped - mmap shared not fixed - mmap shared fixed at somewhere currently unmapped - mmap shared fixed at somewhere already mapped - For private mappings, we should verify that changes cannot be read() - back from the file, nor mmap's back from the file at a different - address. (There have been systems where private was not correctly - implemented like the infamous i386 svr4.0, and systems where the - VM page cache was not coherent with the file system buffer cache - like early versions of FreeBSD and possibly contemporary NetBSD.) - For shared mappings, we should conversely verify that changes get - propagated back to all the places they're supposed to be. - - Grep wants private fixed already mapped. - The main things grep needs to know about mmap are: - * does it exist and is it safe to write into the mmap'd area - * how to use it (BSD variants) */ -#include -#include +else + cat >>confdefs.h <<\_ACEOF +#define HAVE_REALLOC 0 +_ACEOF -#if !STDC_HEADERS && !HAVE_STDLIB_H -char *malloc (); -#endif + case $LIBOBJS in + "realloc.$ac_objext" | \ + *" realloc.$ac_objext" | \ + "realloc.$ac_objext "* | \ + *" realloc.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS realloc.$ac_objext" ;; +esac -/* This mess was copied from the GNU getpagesize.h. */ -#if !HAVE_GETPAGESIZE -/* Assume that all systems that can run configure have sys/param.h. */ -# if !HAVE_SYS_PARAM_H -# define HAVE_SYS_PARAM_H 1 -# endif -# ifdef _SC_PAGESIZE -# define getpagesize() sysconf(_SC_PAGESIZE) -# else /* no _SC_PAGESIZE */ -# if HAVE_SYS_PARAM_H -# include -# ifdef EXEC_PAGESIZE -# define getpagesize() EXEC_PAGESIZE -# else /* no EXEC_PAGESIZE */ -# ifdef NBPG -# define getpagesize() NBPG * CLSIZE -# ifndef CLSIZE -# define CLSIZE 1 -# endif /* no CLSIZE */ -# else /* no NBPG */ -# ifdef NBPC -# define getpagesize() NBPC -# else /* no NBPC */ -# ifdef PAGESIZE -# define getpagesize() PAGESIZE -# endif /* PAGESIZE */ -# endif /* no NBPC */ -# endif /* no NBPG */ -# endif /* no EXEC_PAGESIZE */ -# else /* no HAVE_SYS_PARAM_H */ -# define getpagesize() 8192 /* punt totally */ -# endif /* no HAVE_SYS_PARAM_H */ -# endif /* no _SC_PAGESIZE */ +cat >>confdefs.h <<\_ACEOF +#define realloc rpl_realloc +_ACEOF -#endif /* no HAVE_GETPAGESIZE */ +fi -int -main () -{ - char *data, *data2, *data3; - int i, pagesize; - int fd; - pagesize = getpagesize (); - /* First, make a file with some known garbage in it. */ - data = (char *) malloc (pagesize); - if (!data) - exit (1); - for (i = 0; i < pagesize; ++i) - *(data + i) = rand (); - umask (0); - fd = creat ("conftest.mmap", 0600); - if (fd < 0) - exit (1); - if (write (fd, data, pagesize) != pagesize) - exit (1); - close (fd); - /* Next, try to mmap the file at a fixed address which already has - something else allocated at it. If we can, also make sure that - we see the same garbage. */ - fd = open ("conftest.mmap", O_RDWR); - if (fd < 0) - exit (1); - data2 = (char *) malloc (2 * pagesize); - if (!data2) - exit (1); - data2 += (pagesize - ((long) data2 & (pagesize - 1))) & (pagesize - 1); - if (data2 != mmap (data2, pagesize, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_FIXED, fd, 0L)) - exit (1); - for (i = 0; i < pagesize; ++i) - if (*(data + i) != *(data2 + i)) - exit (1); - /* Finally, make sure that changes to the mapped area do not - percolate back to the file as seen by read(). (This is a bug on - some variants of i386 svr4.0.) */ - for (i = 0; i < pagesize; ++i) - *(data2 + i) = *(data2 + i) + 1; - data3 = (char *) malloc (pagesize); - if (!data3) - exit (1); - if (read (fd, data3, pagesize) != pagesize) - exit (1); - for (i = 0; i < pagesize; ++i) - if (*(data + i) != *(data3 + i)) - exit (1); - close (fd); - exit (0); -} +for ac_header in sys/select.h sys/socket.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ _ACEOF -rm -f conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>&5 +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then - ac_cv_func_mmap_fixed_mapped=yes + ac_header_compiler=yes else - echo "$as_me: program exited with status $ac_status" >&5 -echo "$as_me: failed program was:" >&5 + echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 -( exit $ac_status ) -ac_cv_func_mmap_fixed_mapped=no +ac_header_compiler=no fi -rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no fi -echo "$as_me:$LINENO: result: $ac_cv_func_mmap_fixed_mapped" >&5 -echo "${ECHO_T}$ac_cv_func_mmap_fixed_mapped" >&6 -if test $ac_cv_func_mmap_fixed_mapped = yes; then +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 -cat >>confdefs.h <<\_ACEOF -#define HAVE_MMAP 1 +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## -------------------------------------------------- ## +## Report this to launchd-bug-reports@group.apple.com ## +## -------------------------------------------------- ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi -rm -f conftest.mmap -echo "$as_me:$LINENO: checking return type of signal handlers" >&5 -echo $ECHO_N "checking return type of signal handlers... $ECHO_C" >&6 -if test "${ac_cv_type_signal+set}" = set; then +done + +echo "$as_me:$LINENO: checking types of arguments for select" >&5 +echo $ECHO_N "checking types of arguments for select... $ECHO_C" >&6 +if test "${ac_cv_func_select_args+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else - cat >conftest.$ac_ext <<_ACEOF + for ac_arg234 in 'fd_set *' 'int *' 'void *'; do + for ac_arg1 in 'int' 'size_t' 'unsigned long' 'unsigned'; do + for ac_arg5 in 'struct timeval *' 'const struct timeval *'; do + cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ -#include -#include -#ifdef signal -# undef signal +$ac_includes_default +#if HAVE_SYS_SELECT_H +# include #endif -#ifdef __cplusplus -extern "C" void (*signal (int, void (*)(int)))(int); -#else -void (*signal ()) (); +#if HAVE_SYS_SOCKET_H +# include #endif int main () { -int i; +extern int select ($ac_arg1, + $ac_arg234, $ac_arg234, $ac_arg234, + $ac_arg5); ; return 0; } @@ -5796,33 +7352,47 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then - ac_cv_type_signal=void + ac_cv_func_select_args="$ac_arg1,$ac_arg234,$ac_arg5"; break 3 else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 -ac_cv_type_signal=int fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done + done +done +# Provide a safe default value. +: ${ac_cv_func_select_args='int,int *,struct timeval *'} + fi -echo "$as_me:$LINENO: result: $ac_cv_type_signal" >&5 -echo "${ECHO_T}$ac_cv_type_signal" >&6 +echo "$as_me:$LINENO: result: $ac_cv_func_select_args" >&5 +echo "${ECHO_T}$ac_cv_func_select_args" >&6 +ac_save_IFS=$IFS; IFS=',' +set dummy `echo "$ac_cv_func_select_args" | sed 's/\*/\*/g'` +IFS=$ac_save_IFS +shift cat >>confdefs.h <<_ACEOF -#define RETSIGTYPE $ac_cv_type_signal +#define SELECT_TYPE_ARG1 $1 _ACEOF -echo "$as_me:$LINENO: checking whether lstat dereferences a symlink specified with a trailing slash" >&5 -echo $ECHO_N "checking whether lstat dereferences a symlink specified with a trailing slash... $ECHO_C" >&6 -if test "${ac_cv_func_lstat_dereferences_slashed_symlink+set}" = set; then +cat >>confdefs.h <<_ACEOF +#define SELECT_TYPE_ARG234 ($2) +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define SELECT_TYPE_ARG5 ($3) +_ACEOF + +rm -f conftest* + +echo "$as_me:$LINENO: checking return type of signal handlers" >&5 +echo $ECHO_N "checking return type of signal handlers... $ECHO_C" >&6 +if test "${ac_cv_type_signal+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 -else - rm -f conftest.sym conftest.file -echo >conftest.file -if test "$as_ln_s" = "ln -s" && ln -s conftest.file conftest.sym; then - if test "$cross_compiling" = yes; then - ac_cv_func_lstat_dereferences_slashed_symlink=no else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ @@ -5830,70 +7400,64 @@ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ -$ac_includes_default +#include +#include +#ifdef signal +# undef signal +#endif +#ifdef __cplusplus +extern "C" void (*signal (int, void (*)(int)))(int); +#else +void (*signal ()) (); +#endif + int main () { -struct stat sbuf; - /* Linux will dereference the symlink and fail. - That is better in the sense that it means we will not - have to compile and use the lstat wrapper. */ - exit (lstat ("conftest.sym/", &sbuf) ? 0 : 1); +int i; ; return 0; } _ACEOF -rm -f conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>&5 +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then - ac_cv_func_lstat_dereferences_slashed_symlink=yes + ac_cv_type_signal=void else - echo "$as_me: program exited with status $ac_status" >&5 -echo "$as_me: failed program was:" >&5 + echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 -( exit $ac_status ) -ac_cv_func_lstat_dereferences_slashed_symlink=no -fi -rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext -fi -else - # If the `ln -s' command failed, then we probably don't even - # have an lstat function. - ac_cv_func_lstat_dereferences_slashed_symlink=no +ac_cv_type_signal=int fi -rm -f conftest.sym conftest.file - +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi -echo "$as_me:$LINENO: result: $ac_cv_func_lstat_dereferences_slashed_symlink" >&5 -echo "${ECHO_T}$ac_cv_func_lstat_dereferences_slashed_symlink" >&6 - -test $ac_cv_func_lstat_dereferences_slashed_symlink = yes && +echo "$as_me:$LINENO: result: $ac_cv_type_signal" >&5 +echo "${ECHO_T}$ac_cv_type_signal" >&6 cat >>confdefs.h <<_ACEOF -#define LSTAT_FOLLOWS_SLASHED_SYMLINK 1 +#define RETSIGTYPE $ac_cv_type_signal _ACEOF -if test $ac_cv_func_lstat_dereferences_slashed_symlink = no; then - case $LIBOBJS in - "lstat.$ac_objext" | \ - *" lstat.$ac_objext" | \ - "lstat.$ac_objext "* | \ - *" lstat.$ac_objext "* ) ;; - *) LIBOBJS="$LIBOBJS lstat.$ac_objext" ;; -esac - -fi - echo "$as_me:$LINENO: checking whether stat accepts an empty string" >&5 echo $ECHO_N "checking whether stat accepts an empty string... $ECHO_C" >&6 if test "${ac_cv_func_stat_empty_string_bug+set}" = set; then @@ -5962,7 +7526,23 @@ fi -for ac_func in memmove munmap strerror + + + + + + + + + + + + + + + + +for ac_func in atexit dup2 gethostname gettimeofday memmove memset mkdir munmap rmdir select setenv socket strcasecmp strchr strdup strerror strrchr strstr strtol do as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` echo "$as_me:$LINENO: checking for $ac_func" >&5 @@ -6064,7 +7644,19 @@ fi done - ac_config_files="$ac_config_files Makefile src/Makefile doc/Makefile" + ac_config_files="$ac_config_files Makefile src/Makefile" + + + + +if test "$RC_ProjectName" = launchd_libs; then + LIBS_ONLY_TRUE= + LIBS_ONLY_FALSE='#' +else + LIBS_ONLY_TRUE='#' + LIBS_ONLY_FALSE= +fi + cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure @@ -6164,6 +7756,13 @@ echo "$as_me: error: conditional \"AMDEP\" was never defined. Usually this means the macro was only invoked conditionally." >&2;} { (exit 1); exit 1; }; } fi +if test -z "${LIBS_ONLY_TRUE}" && test -z "${LIBS_ONLY_FALSE}"; then + { { echo "$as_me:$LINENO: error: conditional \"LIBS_ONLY\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +echo "$as_me: error: conditional \"LIBS_ONLY\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi : ${CONFIG_STATUS=./config.status} ac_clean_files_save=$ac_clean_files @@ -6610,7 +8209,6 @@ do # Handling of arguments. "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;; "src/Makefile" ) CONFIG_FILES="$CONFIG_FILES src/Makefile" ;; - "doc/Makefile" ) CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;; "depfiles" ) CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; "src/config.h" ) CONFIG_HEADERS="$CONFIG_HEADERS src/config.h" ;; *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 @@ -6733,7 +8331,10 @@ s,@RANLIB@,$RANLIB,;t t s,@ac_ct_RANLIB@,$ac_ct_RANLIB,;t t s,@CPP@,$CPP,;t t s,@EGREP@,$EGREP,;t t +s,@ALLOCA@,$ALLOCA,;t t s,@LIBOBJS@,$LIBOBJS,;t t +s,@LIBS_ONLY_TRUE@,$LIBS_ONLY_TRUE,;t t +s,@LIBS_ONLY_FALSE@,$LIBS_ONLY_FALSE,;t t s,@LTLIBOBJS@,$LTLIBOBJS,;t t CEOF diff --git a/launchd/configure.ac b/launchd/configure.ac index 331a254..dc665cd 100644 --- a/launchd/configure.ac +++ b/launchd/configure.ac @@ -1,9 +1,10 @@ # -*- Autoconf -*- # Process this file with autoconf to produce a configure script. -AC_PREREQ(2.57) -AC_INIT(launchd, 1.0, launchd-bug-reports@group.apple.com) -AC_CONFIG_SRCDIR([src/ConsoleMessage.c]) +AC_PREREQ(2.59) + +AC_INIT([launchd],[1.0],[launchd-bug-reports@group.apple.com]) +AC_CONFIG_SRCDIR([src/launchd.c]) AM_INIT_AUTOMAKE AM_CONFIG_HEADER([src/config.h]) @@ -11,34 +12,42 @@ AM_CONFIG_HEADER([src/config.h]) AC_PROG_CC AC_PROG_RANLIB -# Checks for libraries. - # Checks for header files. AC_HEADER_DIRENT AC_HEADER_STDC AC_HEADER_SYS_WAIT -AC_CHECK_HEADERS([fcntl.h limits.h mach/mach.h stdlib.h string.h syslog.h unistd.h]) +AC_CHECK_HEADERS([fcntl.h limits.h mach/mach.h netdb.h netinet/in.h paths.h stddef.h stdlib.h string.h sys/ioctl.h sys/mount.h sys/param.h sys/socket.h sys/time.h syslog.h termios.h unistd.h]) # Checks for typedefs, structures, and compiler characteristics. AC_HEADER_STDBOOL AC_C_CONST +AC_TYPE_UID_T AC_TYPE_MODE_T AC_TYPE_OFF_T AC_TYPE_PID_T AC_TYPE_SIZE_T -AC_TYPE_UID_T +AC_HEADER_TIME +AC_STRUCT_TM # Checks for library functions. +AC_FUNC_ALLOCA AC_FUNC_CLOSEDIR_VOID -AC_FUNC_ERROR_AT_LINE AC_FUNC_FORK +AC_PROG_GCC_TRADITIONAL +AC_FUNC_LSTAT +AC_FUNC_LSTAT_FOLLOWS_SLASHED_SYMLINK AC_FUNC_MALLOC +AC_FUNC_MKTIME AC_FUNC_MMAP +AC_FUNC_REALLOC +AC_FUNC_SELECT_ARGTYPES AC_TYPE_SIGNAL AC_FUNC_STAT -AC_CHECK_FUNCS([memmove munmap strerror]) +AC_CHECK_FUNCS([atexit dup2 gethostname gettimeofday memmove memset mkdir munmap rmdir select setenv socket strcasecmp strchr strdup strerror strrchr strstr strtol]) AC_CONFIG_FILES([Makefile - src/Makefile - doc/Makefile]) + src/Makefile]) + +AM_CONDITIONAL(LIBS_ONLY, test "$RC_ProjectName" = launchd_libs) + AC_OUTPUT diff --git a/launchd/doc/Makefile.am b/launchd/doc/Makefile.am deleted file mode 100644 index ce05d62..0000000 --- a/launchd/doc/Makefile.am +++ /dev/null @@ -1,3 +0,0 @@ -install-data-hook: - mkdir -p $(DESTDIR)/System/Library/LaunchDaemons - cp $(srcdir)/com.apple.launchdebugd.xml $(DESTDIR)/System/Library/LaunchDaemons/com.apple.launchd_debugd.plist diff --git a/launchd/doc/Makefile.in b/launchd/doc/Makefile.in deleted file mode 100644 index e42f6db..0000000 --- a/launchd/doc/Makefile.in +++ /dev/null @@ -1,203 +0,0 @@ -# Makefile.in generated by automake 1.6.3 from Makefile.am. -# @configure_input@ - -# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002 -# Free Software Foundation, Inc. -# This Makefile.in is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY, to the extent permitted by law; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. - -@SET_MAKE@ -SHELL = @SHELL@ - -srcdir = @srcdir@ -top_srcdir = @top_srcdir@ -VPATH = @srcdir@ -prefix = @prefix@ -exec_prefix = @exec_prefix@ - -bindir = @bindir@ -sbindir = @sbindir@ -libexecdir = @libexecdir@ -datadir = @datadir@ -sysconfdir = @sysconfdir@ -sharedstatedir = @sharedstatedir@ -localstatedir = @localstatedir@ -libdir = @libdir@ -infodir = @infodir@ -mandir = @mandir@ -includedir = @includedir@ -oldincludedir = /usr/include -pkgdatadir = $(datadir)/@PACKAGE@ -pkglibdir = $(libdir)/@PACKAGE@ -pkgincludedir = $(includedir)/@PACKAGE@ -top_builddir = .. - -ACLOCAL = @ACLOCAL@ -AUTOCONF = @AUTOCONF@ -AUTOMAKE = @AUTOMAKE@ -AUTOHEADER = @AUTOHEADER@ - -am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd -INSTALL = @INSTALL@ -INSTALL_PROGRAM = @INSTALL_PROGRAM@ -INSTALL_DATA = @INSTALL_DATA@ -install_sh_DATA = $(install_sh) -c -m 644 -install_sh_PROGRAM = $(install_sh) -c -install_sh_SCRIPT = $(install_sh) -c -INSTALL_SCRIPT = @INSTALL_SCRIPT@ -INSTALL_HEADER = $(INSTALL_DATA) -transform = @program_transform_name@ -NORMAL_INSTALL = : -PRE_INSTALL = : -POST_INSTALL = : -NORMAL_UNINSTALL = : -PRE_UNINSTALL = : -POST_UNINSTALL = : - -EXEEXT = @EXEEXT@ -OBJEXT = @OBJEXT@ -PATH_SEPARATOR = @PATH_SEPARATOR@ -AMTAR = @AMTAR@ -AWK = @AWK@ -CC = @CC@ -DEPDIR = @DEPDIR@ -INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ -PACKAGE = @PACKAGE@ -RANLIB = @RANLIB@ -STRIP = @STRIP@ -VERSION = @VERSION@ -am__include = @am__include@ -am__quote = @am__quote@ -install_sh = @install_sh@ -subdir = doc -mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs -CONFIG_HEADER = $(top_builddir)/src/config.h -CONFIG_CLEAN_FILES = -DIST_SOURCES = -DIST_COMMON = Makefile.am Makefile.in -all: all-am - -.SUFFIXES: -$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.ac $(ACLOCAL_M4) - cd $(top_srcdir) && \ - $(AUTOMAKE) --gnu doc/Makefile -Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe) -uninstall-info-am: -tags: TAGS -TAGS: - -DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) - -top_distdir = .. -distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) - -distdir: $(DISTFILES) - @list='$(DISTFILES)'; for file in $$list; do \ - if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ - dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ - if test "$$dir" != "$$file" && test "$$dir" != "."; then \ - dir="/$$dir"; \ - $(mkinstalldirs) "$(distdir)$$dir"; \ - else \ - dir=''; \ - fi; \ - if test -d $$d/$$file; then \ - if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ - cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ - fi; \ - cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ - else \ - test -f $(distdir)/$$file \ - || cp -p $$d/$$file $(distdir)/$$file \ - || exit 1; \ - fi; \ - done -check-am: all-am -check: check-am -all-am: Makefile - -installdirs: - -install: install-am -install-exec: install-exec-am -install-data: install-data-am -uninstall: uninstall-am - -install-am: all-am - @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am - -installcheck: installcheck-am -install-strip: - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - INSTALL_STRIP_FLAG=-s \ - `test -z '$(STRIP)' || \ - echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install -mostlyclean-generic: - -clean-generic: - -distclean-generic: - -rm -f Makefile $(CONFIG_CLEAN_FILES) - -maintainer-clean-generic: - @echo "This command is intended for maintainers to use" - @echo "it deletes files that may require special tools to rebuild." -clean: clean-am - -clean-am: clean-generic mostlyclean-am - -distclean: distclean-am - -distclean-am: clean-am distclean-generic - -dvi: dvi-am - -dvi-am: - -info: info-am - -info-am: - -install-data-am: - @$(NORMAL_INSTALL) - $(MAKE) $(AM_MAKEFLAGS) install-data-hook - -install-exec-am: - -install-info: install-info-am - -install-man: - -installcheck-am: - -maintainer-clean: maintainer-clean-am - -maintainer-clean-am: distclean-am maintainer-clean-generic - -mostlyclean: mostlyclean-am - -mostlyclean-am: mostlyclean-generic - -uninstall-am: uninstall-info-am - -.PHONY: all all-am check check-am clean clean-generic distclean \ - distclean-generic distdir dvi dvi-am info info-am install \ - install-am install-data install-data-am install-exec \ - install-exec-am install-info install-info-am install-man \ - install-strip installcheck installcheck-am installdirs \ - maintainer-clean maintainer-clean-generic mostlyclean \ - mostlyclean-generic uninstall uninstall-am uninstall-info-am - -install-data-hook: - mkdir -p $(DESTDIR)/System/Library/LaunchDaemons - cp $(srcdir)/com.apple.launchdebugd.xml $(DESTDIR)/System/Library/LaunchDaemons/com.apple.launchd_debugd.plist -# Tell versions [3.59,3.63) of GNU make to not export all variables. -# Otherwise a system limit (for SysV at least) may be exceeded. -.NOEXPORT: diff --git a/launchd/doc/com.apple.launchdebugd.xml b/launchd/doc/com.apple.launchdebugd.xml deleted file mode 100644 index a79f99f..0000000 --- a/launchd/doc/com.apple.launchdebugd.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - Disabled - - Label - com.apple.launchd_debugd - ProgramArguments - - launchd_debugd - - Sockets - - Listeners - - - SockServiceName - 12345 - SockPassive - - SockType - SOCK_STREAM - - - - ServiceIPC - - - diff --git a/launchd/doc/sampled.c b/launchd/doc/sampled.c index eed5526..6cb8779 100644 --- a/launchd/doc/sampled.c +++ b/launchd/doc/sampled.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -14,7 +15,18 @@ #include "launch.h" -int main(void) +static void +ack_mach_port(launch_data_t o, const char *name, void *context __attribute__((unused))) +{ + mach_port_t p = launch_data_get_machport(o); + + mach_port_deallocate(mach_task_self(), p); + + syslog(LOG_NOTICE, "Ignoring Mach service: %s", name); +} + +int +main(void) { struct timespec timeout = { 60, 0 }; struct sockaddr_storage ss; @@ -38,7 +50,10 @@ int main(void) if (LAUNCH_DATA_ERRNO == launch_data_get_type(resp)) { errno = launch_data_get_errno(resp); - syslog(LOG_ERR, "Check-in failed: %m"); + if (errno == EACCES) + syslog(LOG_ERR, "Check-in failed. Did you forget to set ServiceIPC == true in your plist?"); + else + syslog(LOG_ERR, "Check-in failed: %m"); exit(EXIT_FAILURE); } @@ -46,6 +61,11 @@ int main(void) if (tmp) timeout.tv_sec = launch_data_get_integer(tmp); + tmp = launch_data_dict_lookup(resp, LAUNCH_JOBKEY_MACHSERVICES); + if (tmp) { + launch_data_dict_iterate(tmp, ack_mach_port, NULL); + } + tmp = launch_data_dict_lookup(resp, LAUNCH_JOBKEY_SOCKETS); if (NULL == tmp) { syslog(LOG_ERR, "No sockets found to answer requests on!"); diff --git a/launchd/doc/sampled.plist b/launchd/doc/sampled.plist new file mode 100644 index 0000000..421f480 --- /dev/null +++ b/launchd/doc/sampled.plist @@ -0,0 +1,40 @@ + + + + + + Label + com.example.sampled + + ProgramArguments + + + + sampled + + MachServices + + + + com.apple.sampled.something + + + Sockets + + + + SampleListeners + + + + + SockServiceName + 12345 + + + + + ServiceIPC + + + diff --git a/launchd/src/ConsoleMessage.8 b/launchd/src/ConsoleMessage.8 deleted file mode 100644 index 79f7ddc..0000000 --- a/launchd/src/ConsoleMessage.8 +++ /dev/null @@ -1,105 +0,0 @@ -.Dd April 12, 2002 -.Dt ConsoleMessage 8 -.Os Darwin -.Sh NAME -.Nm ConsoleMessage -.\" The following lines are read in generating the apropos(man -k) database. Use only key -.\" words here as the database is built based on the words here and in the .ND line. -.\" Use .Nm macro to designate other names for the documented program. -.Nd Send a message to -.Nm SystemStarter -.Sh SYNOPSIS -.Nm -.Op Fl v -.Ar message -.Nm -.Op Fl v -.Fl S -.Nm -.Op Fl v -.Fl F -.Nm -.Op Fl v -.Fl s Ar service -.Nm -.Op Fl v -.Fl f Ar service -.Nm -.Op Fl v -.Fl q Ar setting -.Nm -.Op Fl v -.Fl b Ar path -.Nm -.Op Fl v -.Fl u -.Sh DESCRIPTION -The -.Nm -utility may be used by startup item scripts to send messages to -.Nm SystemStarter -(see SystemStarter(8)). -.Pp -The default behavior is to send the specified -.Ar message -to -.Nm SystemStarter -to be displayed on the console. -.Nm SystemStarter -will attempt to localize the string using the current startup item's localization dictionaries. -.Pp -.Nm -may also be used to give feedback to -.Nm SystemStarter -indicating which services provided by the startup item have succeeded or failed. -.Sh OPTIONS -.Bl -tag -width -indent -.It Fl v -verbose mode (prints errors to stdout) -.It Fl S -mark all services provided by this item as successful -.It Fl F -mark all services provided by this item as failed -.It Fl s -mark the -.Ar service -as successful -.It Fl f -mark the -.Ar service -as failed -.It Fl q -query -.Nm SystemStarter -for the value of the configuration variable -.Ar setting -(result will be printed to stdout). -.It Fl b -Tell -.Nm SystemStarter -to load the display bundle at the specified -.Ar path -.It Fl u -Tell -.Nm SystemStarter -to unload the current display bundle -.El -.Sh NOTES -When a -.Ar service -name is not specified, -.Nm -assumes its parent process identification (see getppid(2)) is that of a startup item script, and uses that token to find the correct list of services provided by the current item. If the process calling -.Nm -is not a startup item script, it is necessary to explicitly provide -.Ar service -names. -.Sh SEE ALSO -.\" List links in ascending order by section, alphabetically within a section. -.\" Please do not reference files that do not exist without filing a bug report -.Xr SystemStarter 8 -.\" .Sh BUGS \" Document known, unremedied bugs -.Sh HISTORY -The -.Nm -utility appeared in Darwin 6.0 diff --git a/launchd/src/ConsoleMessage.c b/launchd/src/ConsoleMessage.c deleted file mode 100644 index 30f1cf6..0000000 --- a/launchd/src/ConsoleMessage.c +++ /dev/null @@ -1,327 +0,0 @@ -/** - * ConsoleMessage.c - ConsoleMessage main - * Wilfredo Sanchez | wsanchez@opensource.apple.com - * Kevin Van Vechten | kevinvv@uclink4.berkeley.edu - * $Apple$ - ** - * Copyright (c) 1999-2002 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights - * Reserved. This file contains Original Code and/or Modifications of - * Original Code as defined in and that 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. - * - * The 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@ - ** - * The ConsoleMessage utility sends an IPC message to SystemStarter - * containing the message specified on the command line. SystemStarter - * will perform the localization. The message is also printed to - * the system log. - **/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "SystemStarterIPC.h" - -static CFDataRef sendIPCMessage(CFStringRef aPortName, CFDataRef aData, CFStringRef aRunLoopMode); - -static void usage() __attribute__((__noreturn__)); -static void -usage() -{ - /* char* aProgram = **_NSGetArgv(); */ - fprintf(stderr, "usage:\n" - "\tConsoleMessage [-v] \n" - "\tConsoleMessage [-v] -S\n" - "\tConsoleMessage [-v] -F\n" - "\tConsoleMessage [-v] -s \n" - "\tConsoleMessage [-v] -f \n" - "\tConsoleMessage [-v] -q \n" - "\tConsoleMessage [-v] -b \n" - "\tConsoleMessage [-v] -u\n" - "\noptions:\n" - "\t-v: verbose (prints errors to stdout)\n" - "\t-S: mark all services as successful\n" - "\t-F: mark all services as failed\n" - "\t-s: mark a specific service as successful\n" - "\t-f: mark a specific service as failed\n" - "\t-q: query a configuration setting\n" - "\t-b: (ignored)\n" - "\t-u: (ignored)\n"); - exit(1); -} - -enum { - kActionConsoleMessage, - kActionSuccess, - kActionFailure, - kActionQuery, -}; - -int -main(int argc, char *argv[]) -{ - int anExitCode = 0; - int aVerboseFlag = 0; - int anAction = kActionConsoleMessage; - char *aProgram = argv[0]; - char *anArgCStr = NULL; - char c; - pid_t w4lw_pid = 0; - FILE *w4lw_f; - - /** - * Handle command line. - **/ - while ((c = getopt(argc, argv, "?vSFs:f:q:b:u")) != -1) { - switch (c) { - case '?': - usage(); - break; - case 'v': - aVerboseFlag = 1; - break; - case 'S': - anAction = kActionSuccess; - anArgCStr = NULL; - break; - case 'F': - anAction = kActionFailure; - anArgCStr = NULL; - break; - case 's': - anAction = kActionSuccess; - anArgCStr = optarg; - break; - case 'f': - anAction = kActionFailure; - anArgCStr = optarg; - break; - case 'q': - anAction = kActionQuery; - anArgCStr = optarg; - break; - case 'b': - exit(EXIT_SUCCESS); - break; - case 'u': - w4lw_f = fopen("/var/run/waiting4loginwindow.pid", "r"); - if (w4lw_f) { - fscanf(w4lw_f, "%d\n", &w4lw_pid); - if (w4lw_pid) - kill(w4lw_pid, SIGTERM); - } - exit(EXIT_SUCCESS); - break; - default: - fprintf(stderr, "ignoring unknown option '-%c'\n", c); - break; - } - } - argc -= optind; - argv += optind; - - if ((anAction == kActionConsoleMessage && argc != 1) || - (anAction == kActionSuccess && argc != 0) || - (anAction == kActionFailure && argc != 0) || - (anAction == kActionQuery && argc != 0)) { - usage(); - } - if (getuid() != 0) { - fprintf(stderr, "you must be root to run %s\n", aProgram); - exit(1); - } else { - CFMutableDictionaryRef anIPCMessage = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - - if (anIPCMessage) { - CFStringRef anArg = NULL; - CFIndex aPID = getppid(); - CFNumberRef aPIDNumber = CFNumberCreate(NULL, kCFNumberCFIndexType, &aPID); - - /* - * Parent process id is the process id of the startup - * item that called ConsoleMessage. - */ - CFDictionarySetValue(anIPCMessage, kIPCProcessIDKey, aPIDNumber); - CFRelease(aPIDNumber); - - if (anArgCStr) { - anArg = CFStringCreateWithCString(NULL, anArgCStr, kCFStringEncodingUTF8); - } - if (anAction == kActionSuccess || anAction == kActionFailure) { - CFBooleanRef aStatus = (anAction == kActionSuccess) ? kCFBooleanTrue : kCFBooleanFalse; - CFDictionarySetValue(anIPCMessage, kIPCMessageKey, kIPCStatusMessage); - CFDictionarySetValue(anIPCMessage, kIPCStatusKey, aStatus); - if (anArg) - CFDictionarySetValue(anIPCMessage, kIPCServiceNameKey, anArg); - } else if (anAction == kActionQuery && anArg) { - CFDictionarySetValue(anIPCMessage, kIPCMessageKey, kIPCQueryMessage); - CFDictionarySetValue(anIPCMessage, kIPCConfigSettingKey, anArg); - } else if (anAction == kActionConsoleMessage) { - char *aConsoleMessageCStr = argv[0]; - CFStringRef aConsoleMessage = CFStringCreateWithCString(NULL, aConsoleMessageCStr, kCFStringEncodingUTF8); - - syslog(LOG_INFO, "%s", aConsoleMessageCStr); - - CFDictionarySetValue(anIPCMessage, kIPCMessageKey, kIPCConsoleMessage); - CFDictionarySetValue(anIPCMessage, kIPCConsoleMessageKey, aConsoleMessage); - CFRelease(aConsoleMessage); - } - if (anArg) - CFRelease(anArg); - - { - CFDataRef aData = CFPropertyListCreateXMLData(NULL, anIPCMessage); - if (aData) { - CFDataRef aResultData = sendIPCMessage(CFSTR(kSystemStarterMessagePort), aData, kCFRunLoopDefaultMode); - - /* aResultData should be ASCIZ */ - if (aResultData) { - fprintf(stdout, "%s", CFDataGetBytePtr(aResultData)); - CFRelease(aResultData); - } else { - char *aConsoleMessageCStr = argv[0]; - fprintf(stdout, "%s\n", aConsoleMessageCStr); - - if (aVerboseFlag) - fprintf(stderr, "%s could not connect to SystemStarter.\n", aProgram); - anExitCode = 0; - } - CFRelease(aData); - } else { - if (aVerboseFlag) - fprintf(stderr, "%s: not enough memory to create IPC message.\n", aProgram); - anExitCode = 1; - } - } - } else { - if (aVerboseFlag) - fprintf(stderr, "%s: not enough memory to create IPC message.\n", aProgram); - anExitCode = 1; - } - } - exit(anExitCode); -} - - -static void -dummyCallback(void) -{ -} - -static void replyCallback(CFMachPortRef port __attribute__((unused)), void *aPtr, CFIndex aSize __attribute__((unused)), CFDataRef * aReply) { - SystemStarterIPCMessage *aMessage = (SystemStarterIPCMessage *) aPtr; - - if (aReply != NULL && - aMessage->aProtocol == kIPCProtocolVersion && - aMessage->aByteLength >= 0) { - *aReply = CFDataCreate(NULL, (UInt8 *) aMessage + aMessage->aByteLength, aMessage->aByteLength); - } else if (aReply != NULL) { - *aReply = NULL; - } -} - - -static CFDataRef -sendIPCMessage(CFStringRef aPortName, CFDataRef aData, CFStringRef aRunLoopMode) -{ - SystemStarterIPCMessage *aMessage = NULL; - CFRunLoopSourceRef aSource = NULL; - CFMachPortRef aMachPort = NULL, aReplyPort = NULL; - CFMachPortContext aContext; - kern_return_t aKernReturn = KERN_FAILURE; - mach_port_t aBootstrapPort, aNativePort; - char *aPortNameUTF8String; - CFDataRef aReply = NULL; - SInt32 aStrLen; - - aContext.version = 0; - aContext.info = (void *) NULL; - aContext.retain = 0; - aContext.release = 0; - aContext.copyDescription = 0; - - /* Look up the remote port by name */ - - aStrLen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(aPortName) + 1, kCFStringEncodingUTF8); - aPortNameUTF8String = malloc(aStrLen); - if (aPortNameUTF8String) { - CFStringGetCString(aPortName, aPortNameUTF8String, aStrLen, kCFStringEncodingUTF8); - task_get_bootstrap_port(mach_task_self(), &aBootstrapPort); - aKernReturn = bootstrap_look_up(aBootstrapPort, aPortNameUTF8String, &aNativePort); - aMachPort = (KERN_SUCCESS == aKernReturn) ? CFMachPortCreateWithPort(NULL, aNativePort, (CFMachPortCallBack) dummyCallback, &aContext, NULL) : NULL; - free(aPortNameUTF8String); - } - /* Create a reply port and associated run loop source */ - aContext.info = &aReply; - aReplyPort = CFMachPortCreate(NULL, (CFMachPortCallBack) replyCallback, &aContext, NULL); - if (aReplyPort) { - aSource = CFMachPortCreateRunLoopSource(NULL, aReplyPort, 0); - if (aSource) { - CFRunLoopAddSource(CFRunLoopGetCurrent(), aSource, aRunLoopMode); - } - } - /* Allocate a buffer for the message */ - if (aData && aMachPort && aReplyPort) { - SInt32 aSize = (sizeof(SystemStarterIPCMessage) + (CFDataGetLength(aData) + 3)) & ~0x3; - aMessage = (SystemStarterIPCMessage *) malloc(aSize); - if (aMessage) { - aMessage->aHeader.msgh_id = 1; - aMessage->aHeader.msgh_size = aSize; - aMessage->aHeader.msgh_remote_port = CFMachPortGetPort(aMachPort); - aMessage->aHeader.msgh_local_port = CFMachPortGetPort(aReplyPort); - aMessage->aHeader.msgh_reserved = 0; - aMessage->aHeader.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE); - aMessage->aBody.msgh_descriptor_count = 0; - aMessage->aProtocol = kIPCProtocolVersion; - aMessage->aByteLength = CFDataGetLength(aData); - memmove((uint8_t *) aMessage + sizeof(SystemStarterIPCMessage), - CFDataGetBytePtr(aData), - CFDataGetLength(aData)); - } - } - /* Wait up to 1 second to send the message */ - if (aMessage) { - aKernReturn = mach_msg((mach_msg_header_t *) aMessage, MACH_SEND_MSG | MACH_SEND_TIMEOUT, aMessage->aHeader.msgh_size, 0, MACH_PORT_NULL, 1000.0, MACH_PORT_NULL); - free(aMessage); - } - /* Wait up to 30 seconds for the reply */ - if (aSource && aKernReturn == MACH_MSG_SUCCESS) { - CFRetain(aReplyPort); - CFRunLoopRunInMode(aRunLoopMode, 30.0, true); - /* - * aReplyPort's replyCallback will set the local aReply - * variable - */ - CFRelease(aReplyPort); - } - if (aMachPort) - CFRelease(aMachPort); - if (aReplyPort) - CFRelease(aReplyPort); - if (aSource) { - CFRunLoopRemoveSource(CFRunLoopGetCurrent(), aSource, aRunLoopMode); - CFRelease(aSource); - } - return aReply; -} diff --git a/launchd/src/IPC.c b/launchd/src/IPC.c index ac3a6ce..c82b1f0 100644 --- a/launchd/src/IPC.c +++ b/launchd/src/IPC.c @@ -6,34 +6,31 @@ ** * Copyright (c) 1999-2002 Apple Computer, Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ - * - * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights - * Reserved. This file contains Original Code and/or Modifications of - * Original Code as defined in and that 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. - * - * The 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@ + * @APPLE_APACHE_LICENSE_HEADER_START@ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @APPLE_APACHE_LICENSE_HEADER_END@ **/ #include #include #include #include -#include #include #include + +#include "bootstrap_public.h" #include "IPC.h" #include "StartupItems.h" #include "SystemStarter.h" @@ -370,9 +367,7 @@ CreateIPCRunLoopSource(CFStringRef aPortName, StartupContext aStartupContext) aBuffer, aPortNameSize, kCFStringEncodingUTF8)) { - mach_port_t aBootstrapPort; - task_get_bootstrap_port(mach_task_self(), &aBootstrapPort); - aKernReturn = bootstrap_register(aBootstrapPort, aBuffer, CFMachPortGetPort(aMachPort)); + aKernReturn = bootstrap_register(bootstrap_port, aBuffer, CFMachPortGetPort(aMachPort)); } if (aBuffer) CFAllocatorDeallocate(NULL, aBuffer); diff --git a/launchd/src/IPC.h b/launchd/src/IPC.h index 13e49d5..1670e02 100644 --- a/launchd/src/IPC.h +++ b/launchd/src/IPC.h @@ -6,25 +6,21 @@ ** * Copyright (c) 1999-2001 Apple Computer, Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ + * @APPLE_APACHE_LICENSE_HEADER_START@ * - * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights - * Reserved. This file contains Original Code and/or Modifications of - * Original Code as defined in and that 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * The 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. + * http://www.apache.org/licenses/LICENSE-2.0 * - * @APPLE_LICENSE_HEADER_END@ + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @APPLE_APACHE_LICENSE_HEADER_END@ **/ #ifndef _IPC_H_ diff --git a/launchd/src/Makefile.am b/launchd/src/Makefile.am index b27efc5..a2d51ee 100644 --- a/launchd/src/Makefile.am +++ b/launchd/src/Makefile.am @@ -1,51 +1,69 @@ -AM_CFLAGS = -no-cpp-precomp -F/System/Library/PrivateFrameworks -Wall -W -Wshadow -Wpadded -Werror -fconstant-cfstrings +AM_CFLAGS = -no-cpp-precomp -F/System/Library/PrivateFrameworks -Wall -W -Wshadow -Wmissing-prototypes -Wmissing-declarations -Werror -D__MigTypeCheck=1 -sbin_SCRIPTS = service +CLEANFILES = bootstrap.h bootstrapServer.c bootstrapUser.c bootstrapServer.h notifyServer.c notifyServer.h mpm_replyUser.c mpm_reply.h + +bootstrap.h bootstrapServer.c bootstrapUser.c bootstrapServer.h: $(srcdir)/bootstrap.defs + mig $(MIGFLAGS) -sheader bootstrapServer.h $(srcdir)/bootstrap.defs + +if LIBS_ONLY noinst_LIBRARIES = liblaunch.a liblaunch_profile.a -bin_PROGRAMS = launchctl wait4path -sbin_PROGRAMS = launchd SystemStarter launchd_debugd -libexec_PROGRAMS = ConsoleMessage register_mach_bootstrap_servers StartupItemContext launchproxy +liblaunch_a_SOURCES = liblaunch.c bootstrap_public.c -sysconf_DATA = hostconfig rc rc.common rc.netboot rc.shutdown +liblaunch_profile_a_CFLAGS = -pg $(AM_CFLAGS) +liblaunch_profile_a_SOURCES = liblaunch.c bootstrap_public.c -ConsoleMessage_LDFLAGS = -framework CoreFoundation +bootstrap_public.c:: bootstrap.h -launchctl_LDFLAGS = -framework CoreFoundation -weak_library /usr/lib/libedit.dylib +install-data-hook: + mkdir -p $(DESTDIR)/usr/local/lib/system + cp liblaunch.a $(DESTDIR)/usr/local/lib/system + cp liblaunch.a $(DESTDIR)/usr/local/lib/system/liblaunch_debug.a + cp liblaunch_profile.a $(DESTDIR)/usr/local/lib/system/liblaunch_profile.a -register_mach_bootstrap_servers_LDFLAGS = -framework CoreFoundation +else -launchd_debugd_CFLAGS = -mdynamic-no-pic $(AM_CFLAGS) -launchd_debugd_SOURCES = launchdebugd.c +sbin_SCRIPTS = service -liblaunch_a_SOURCES = liblaunch.c +bin_PROGRAMS = launchctl wait4path +sbin_PROGRAMS = launchd SystemStarter +libexec_PROGRAMS = launchproxy -liblaunch_profile_a_CFLAGS = -pg $(AM_CFLAGS) -liblaunch_profile_a_SOURCES = liblaunch.c +sysconf_DATA = hostconfig rc.common rc.netboot rc.shutdown -SystemStarter_CFLAGS = -mdynamic-no-pic $(AM_CFLAGS) +launchctl_CFLAGS = $(AM_CFLAGS) -fvisibility=hidden +launchctl_LDFLAGS = -framework CoreFoundation -weak_library /usr/lib/libedit.dylib + +SystemStarter_CFLAGS = -mdynamic-no-pic $(AM_CFLAGS) -fvisibility=hidden SystemStarter_LDFLAGS = -framework CoreFoundation SystemStarter_SOURCES = StartupItems.c IPC.c SystemStarter.c -launchd_CFLAGS = -DPID1_REAP_ADOPTED_CHILDREN -mdynamic-no-pic $(AM_CFLAGS) -Wno-unused-parameter +launchd_CFLAGS = -mdynamic-no-pic $(AM_CFLAGS) -Wno-unused-parameter -fvisibility=hidden launchd_LDFLAGS = -lbsm -launchd_SOURCES = launchd.c init.c bootstrap.c lists.c rpc_services.c bootstrapServer.c +launchd_SOURCES = launchd.c launchd_core_logic.c launchd_unix_ipc.c launchd_mach_ipc.c init.c bootstrapServer.c notifyServer.c launchd_internalUser.c launchd_internalServer.c mpm_replyUser.c + +launchd_mach_ipc.c:: bootstrap.h bootstrapServer.h notifyServer.h launchd_internal.h +launchd_core_logic.c:: bootstrap.h mpm_reply.h +launchd.c:: bootstrap.h launchd_internalServer.h launchd_internal.h notifyServer.h bootstrapServer.h launchproxy_LDFLAGS = -weak_framework Security -CLEANFILES = bootstrap.h bootstrapServer.c bootstrapUser.c +notifyServer.c notifyServer.h: /usr/include/mach/notify.defs + mig $(MIGFLAGS) -header /dev/null -user /dev/null -sheader notifyServer.h /usr/include/mach/notify.defs -bootstrap.c:: bootstrap.h +mpm_replyUser.c mpm_reply.h: $(srcdir)/mpm_reply.defs + mig $(MIGFLAGS) -sheader /dev/null -server /dev/null $(srcdir)/mpm_reply.defs + +launchd_internal.h launchd_internalServer.c launchd_internalUser.c launchd_internalServer.h: $(srcdir)/launchd_internal.defs + mig $(MIGFLAGS) -sheader launchd_internalServer.h $(srcdir)/launchd_internal.defs -bootstrap.h bootstrapServer.c bootstrapUser.c: $(srcdir)/bootstrap.defs - mig $(MIGFLAGS) $(srcdir)/bootstrap.defs man1_MANS = wait4path.1 launchctl.1 man5_MANS = launchd.plist.5 launchd.conf.5 -man8_MANS = ConsoleMessage.8 StartupItemContext.8 SystemStarter.8 init.8 rc.8 launchd.8 service.8 launchproxy.8 launchd_debugd.8 +man8_MANS = StartupItemContext.8 SystemStarter.8 rc.8 launchd.8 service.8 launchproxy.8 STARTUPITEMS = $(basename $(notdir $(wildcard $(srcdir)/StartupItems/*.plist))) @@ -58,12 +76,12 @@ $(addprefix $(DESTDIR)/System/Library/StartupItems/, $(STARTUPITEMS)): install-startupitems: $(addprefix $(DESTDIR)/System/Library/StartupItems/, $(STARTUPITEMS)) install-data-hook: install-startupitems - mkdir -p $(DESTDIR)/usr/local/lib/system - cp liblaunch.a $(DESTDIR)/usr/local/lib/system - cp liblaunch.a $(DESTDIR)/usr/local/lib/system/liblaunch_debug.a - cp liblaunch_profile.a $(DESTDIR)/usr/local/lib/system/liblaunch_profile.a - mkdir -p $(DESTDIR)/usr/include + mkdir -p $(DESTDIR)/usr/libexec + cp $(srcdir)/StartupItemContext $(DESTDIR)/usr/libexec + mkdir -p $(DESTDIR)/usr/include/servers cp $(srcdir)/launch.h $(DESTDIR)/usr/include + cp $(srcdir)/bootstrap_public.h $(DESTDIR)/usr/include/servers/bootstrap.h + cp $(srcdir)/bootstrap_public.h $(DESTDIR)/usr/include/servers/bootstrap_defs.h mkdir -p $(DESTDIR)/usr/local/include cp $(srcdir)/launch_priv.h $(DESTDIR)/usr/local/include mkdir -p $(DESTDIR)/$(sysconfdir)/mach_init.d @@ -73,5 +91,4 @@ install-data-hook: install-startupitems mkdir -p $(DESTDIR)/System/Library/LaunchDaemons mkdir -p $(DESTDIR)/System/Library/LaunchAgents -install-exec-hook: - chmod u+s $(DESTDIR)/$(sbindir)/launchd +endif diff --git a/launchd/src/Makefile.in b/launchd/src/Makefile.in index 239c295..a93b435 100644 --- a/launchd/src/Makefile.in +++ b/launchd/src/Makefile.in @@ -75,51 +75,45 @@ VERSION = @VERSION@ am__include = @am__include@ am__quote = @am__quote@ install_sh = @install_sh@ -AM_CFLAGS = -no-cpp-precomp -F/System/Library/PrivateFrameworks -Wall -W -Wshadow -Wpadded -Werror -fconstant-cfstrings +AM_CFLAGS = -no-cpp-precomp -F/System/Library/PrivateFrameworks -Wall -W -Wshadow -Wmissing-prototypes -Wmissing-declarations -Werror -D__MigTypeCheck=1 -sbin_SCRIPTS = service +CLEANFILES = bootstrap.h bootstrapServer.c bootstrapUser.c bootstrapServer.h notifyServer.c notifyServer.h mpm_replyUser.c mpm_reply.h -noinst_LIBRARIES = liblaunch.a liblaunch_profile.a +@LIBS_ONLY_TRUE@noinst_LIBRARIES = liblaunch.a liblaunch_profile.a -bin_PROGRAMS = launchctl wait4path -sbin_PROGRAMS = launchd SystemStarter launchd_debugd -libexec_PROGRAMS = ConsoleMessage register_mach_bootstrap_servers StartupItemContext launchproxy +@LIBS_ONLY_TRUE@liblaunch_a_SOURCES = liblaunch.c bootstrap_public.c -sysconf_DATA = hostconfig rc rc.common rc.netboot rc.shutdown +@LIBS_ONLY_TRUE@liblaunch_profile_a_CFLAGS = -pg $(AM_CFLAGS) +@LIBS_ONLY_TRUE@liblaunch_profile_a_SOURCES = liblaunch.c bootstrap_public.c -ConsoleMessage_LDFLAGS = -framework CoreFoundation +@LIBS_ONLY_FALSE@sbin_SCRIPTS = service -launchctl_LDFLAGS = -framework CoreFoundation -weak_library /usr/lib/libedit.dylib +@LIBS_ONLY_FALSE@bin_PROGRAMS = launchctl wait4path +@LIBS_ONLY_FALSE@sbin_PROGRAMS = launchd SystemStarter +@LIBS_ONLY_FALSE@libexec_PROGRAMS = launchproxy -register_mach_bootstrap_servers_LDFLAGS = -framework CoreFoundation +@LIBS_ONLY_FALSE@sysconf_DATA = hostconfig rc.common rc.netboot rc.shutdown -launchd_debugd_CFLAGS = -mdynamic-no-pic $(AM_CFLAGS) -launchd_debugd_SOURCES = launchdebugd.c +@LIBS_ONLY_FALSE@launchctl_CFLAGS = $(AM_CFLAGS) -fvisibility=hidden +@LIBS_ONLY_FALSE@launchctl_LDFLAGS = -framework CoreFoundation -weak_library /usr/lib/libedit.dylib -liblaunch_a_SOURCES = liblaunch.c +@LIBS_ONLY_FALSE@SystemStarter_CFLAGS = -mdynamic-no-pic $(AM_CFLAGS) -fvisibility=hidden +@LIBS_ONLY_FALSE@SystemStarter_LDFLAGS = -framework CoreFoundation +@LIBS_ONLY_FALSE@SystemStarter_SOURCES = StartupItems.c IPC.c SystemStarter.c -liblaunch_profile_a_CFLAGS = -pg $(AM_CFLAGS) -liblaunch_profile_a_SOURCES = liblaunch.c +@LIBS_ONLY_FALSE@launchd_CFLAGS = -mdynamic-no-pic $(AM_CFLAGS) -Wno-unused-parameter -fvisibility=hidden +@LIBS_ONLY_FALSE@launchd_LDFLAGS = -lbsm +@LIBS_ONLY_FALSE@launchd_SOURCES = launchd.c launchd_core_logic.c launchd_unix_ipc.c launchd_mach_ipc.c init.c bootstrapServer.c notifyServer.c launchd_internalUser.c launchd_internalServer.c mpm_replyUser.c -SystemStarter_CFLAGS = -mdynamic-no-pic $(AM_CFLAGS) -SystemStarter_LDFLAGS = -framework CoreFoundation -SystemStarter_SOURCES = StartupItems.c IPC.c SystemStarter.c +@LIBS_ONLY_FALSE@launchproxy_LDFLAGS = -weak_framework Security -launchd_CFLAGS = -DPID1_REAP_ADOPTED_CHILDREN -mdynamic-no-pic $(AM_CFLAGS) -Wno-unused-parameter -launchd_LDFLAGS = -lbsm -launchd_SOURCES = launchd.c init.c bootstrap.c lists.c rpc_services.c bootstrapServer.c +@LIBS_ONLY_FALSE@man1_MANS = wait4path.1 launchctl.1 -launchproxy_LDFLAGS = -weak_framework Security +@LIBS_ONLY_FALSE@man5_MANS = launchd.plist.5 launchd.conf.5 -CLEANFILES = bootstrap.h bootstrapServer.c bootstrapUser.c +@LIBS_ONLY_FALSE@man8_MANS = StartupItemContext.8 SystemStarter.8 rc.8 launchd.8 service.8 launchproxy.8 -man1_MANS = wait4path.1 launchctl.1 - -man5_MANS = launchd.plist.5 launchd.conf.5 - -man8_MANS = ConsoleMessage.8 StartupItemContext.8 SystemStarter.8 init.8 rc.8 launchd.8 service.8 launchproxy.8 launchd_debugd.8 - -STARTUPITEMS = $(basename $(notdir $(wildcard $(srcdir)/StartupItems/*.plist))) +@LIBS_ONLY_FALSE@STARTUPITEMS = $(basename $(notdir $(wildcard $(srcdir)/StartupItems/*.plist))) subdir = src mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs CONFIG_HEADER = config.h @@ -128,61 +122,51 @@ LIBRARIES = $(noinst_LIBRARIES) liblaunch_a_AR = $(AR) cru liblaunch_a_LIBADD = -am_liblaunch_a_OBJECTS = liblaunch.$(OBJEXT) +@LIBS_ONLY_TRUE@am_liblaunch_a_OBJECTS = liblaunch.$(OBJEXT) \ +@LIBS_ONLY_TRUE@ bootstrap_public.$(OBJEXT) liblaunch_a_OBJECTS = $(am_liblaunch_a_OBJECTS) liblaunch_profile_a_AR = $(AR) cru liblaunch_profile_a_LIBADD = -am_liblaunch_profile_a_OBJECTS = liblaunch_profile_a-liblaunch.$(OBJEXT) +@LIBS_ONLY_TRUE@am_liblaunch_profile_a_OBJECTS = \ +@LIBS_ONLY_TRUE@ liblaunch_profile_a-liblaunch.$(OBJEXT) \ +@LIBS_ONLY_TRUE@ liblaunch_profile_a-bootstrap_public.$(OBJEXT) liblaunch_profile_a_OBJECTS = $(am_liblaunch_profile_a_OBJECTS) -bin_PROGRAMS = launchctl$(EXEEXT) wait4path$(EXEEXT) -libexec_PROGRAMS = ConsoleMessage$(EXEEXT) \ - register_mach_bootstrap_servers$(EXEEXT) \ - StartupItemContext$(EXEEXT) launchproxy$(EXEEXT) -sbin_PROGRAMS = launchd$(EXEEXT) SystemStarter$(EXEEXT) \ - launchd_debugd$(EXEEXT) +@LIBS_ONLY_TRUE@bin_PROGRAMS = +@LIBS_ONLY_FALSE@bin_PROGRAMS = launchctl$(EXEEXT) wait4path$(EXEEXT) +@LIBS_ONLY_TRUE@libexec_PROGRAMS = +@LIBS_ONLY_FALSE@libexec_PROGRAMS = launchproxy$(EXEEXT) +@LIBS_ONLY_TRUE@sbin_PROGRAMS = +@LIBS_ONLY_FALSE@sbin_PROGRAMS = launchd$(EXEEXT) SystemStarter$(EXEEXT) PROGRAMS = $(bin_PROGRAMS) $(libexec_PROGRAMS) $(sbin_PROGRAMS) -ConsoleMessage_SOURCES = ConsoleMessage.c -ConsoleMessage_OBJECTS = ConsoleMessage.$(OBJEXT) -ConsoleMessage_LDADD = $(LDADD) -ConsoleMessage_DEPENDENCIES = -StartupItemContext_SOURCES = StartupItemContext.c -StartupItemContext_OBJECTS = StartupItemContext.$(OBJEXT) -StartupItemContext_LDADD = $(LDADD) -StartupItemContext_DEPENDENCIES = -StartupItemContext_LDFLAGS = -am_SystemStarter_OBJECTS = SystemStarter-StartupItems.$(OBJEXT) \ - SystemStarter-IPC.$(OBJEXT) \ - SystemStarter-SystemStarter.$(OBJEXT) +@LIBS_ONLY_FALSE@am_SystemStarter_OBJECTS = \ +@LIBS_ONLY_FALSE@ SystemStarter-StartupItems.$(OBJEXT) \ +@LIBS_ONLY_FALSE@ SystemStarter-IPC.$(OBJEXT) \ +@LIBS_ONLY_FALSE@ SystemStarter-SystemStarter.$(OBJEXT) SystemStarter_OBJECTS = $(am_SystemStarter_OBJECTS) SystemStarter_LDADD = $(LDADD) SystemStarter_DEPENDENCIES = launchctl_SOURCES = launchctl.c -launchctl_OBJECTS = launchctl.$(OBJEXT) +launchctl_OBJECTS = launchctl-launchctl.$(OBJEXT) launchctl_LDADD = $(LDADD) launchctl_DEPENDENCIES = -am_launchd_OBJECTS = launchd-launchd.$(OBJEXT) launchd-init.$(OBJEXT) \ - launchd-bootstrap.$(OBJEXT) launchd-lists.$(OBJEXT) \ - launchd-rpc_services.$(OBJEXT) \ - launchd-bootstrapServer.$(OBJEXT) +@LIBS_ONLY_FALSE@am_launchd_OBJECTS = launchd-launchd.$(OBJEXT) \ +@LIBS_ONLY_FALSE@ launchd-launchd_core_logic.$(OBJEXT) \ +@LIBS_ONLY_FALSE@ launchd-launchd_unix_ipc.$(OBJEXT) \ +@LIBS_ONLY_FALSE@ launchd-launchd_mach_ipc.$(OBJEXT) \ +@LIBS_ONLY_FALSE@ launchd-init.$(OBJEXT) \ +@LIBS_ONLY_FALSE@ launchd-bootstrapServer.$(OBJEXT) \ +@LIBS_ONLY_FALSE@ launchd-notifyServer.$(OBJEXT) \ +@LIBS_ONLY_FALSE@ launchd-launchd_internalUser.$(OBJEXT) \ +@LIBS_ONLY_FALSE@ launchd-launchd_internalServer.$(OBJEXT) \ +@LIBS_ONLY_FALSE@ launchd-mpm_replyUser.$(OBJEXT) launchd_OBJECTS = $(am_launchd_OBJECTS) launchd_LDADD = $(LDADD) launchd_DEPENDENCIES = -am_launchd_debugd_OBJECTS = launchd_debugd-launchdebugd.$(OBJEXT) -launchd_debugd_OBJECTS = $(am_launchd_debugd_OBJECTS) -launchd_debugd_LDADD = $(LDADD) -launchd_debugd_DEPENDENCIES = -launchd_debugd_LDFLAGS = launchproxy_SOURCES = launchproxy.c launchproxy_OBJECTS = launchproxy.$(OBJEXT) launchproxy_LDADD = $(LDADD) launchproxy_DEPENDENCIES = -register_mach_bootstrap_servers_SOURCES = \ - register_mach_bootstrap_servers.c -register_mach_bootstrap_servers_OBJECTS = \ - register_mach_bootstrap_servers.$(OBJEXT) -register_mach_bootstrap_servers_LDADD = $(LDADD) -register_mach_bootstrap_servers_DEPENDENCIES = wait4path_SOURCES = wait4path.c wait4path_OBJECTS = wait4path.$(OBJEXT) wait4path_LDADD = $(LDADD) @@ -198,22 +182,24 @@ LDFLAGS = @LDFLAGS@ LIBS = @LIBS@ depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles -@AMDEP_TRUE@DEP_FILES = ./$(DEPDIR)/ConsoleMessage.Po \ -@AMDEP_TRUE@ ./$(DEPDIR)/StartupItemContext.Po \ -@AMDEP_TRUE@ ./$(DEPDIR)/SystemStarter-IPC.Po \ +@AMDEP_TRUE@DEP_FILES = ./$(DEPDIR)/SystemStarter-IPC.Po \ @AMDEP_TRUE@ ./$(DEPDIR)/SystemStarter-StartupItems.Po \ @AMDEP_TRUE@ ./$(DEPDIR)/SystemStarter-SystemStarter.Po \ -@AMDEP_TRUE@ ./$(DEPDIR)/launchctl.Po \ -@AMDEP_TRUE@ ./$(DEPDIR)/launchd-bootstrap.Po \ +@AMDEP_TRUE@ ./$(DEPDIR)/bootstrap_public.Po \ +@AMDEP_TRUE@ ./$(DEPDIR)/launchctl-launchctl.Po \ @AMDEP_TRUE@ ./$(DEPDIR)/launchd-bootstrapServer.Po \ @AMDEP_TRUE@ ./$(DEPDIR)/launchd-init.Po \ @AMDEP_TRUE@ ./$(DEPDIR)/launchd-launchd.Po \ -@AMDEP_TRUE@ ./$(DEPDIR)/launchd-lists.Po \ -@AMDEP_TRUE@ ./$(DEPDIR)/launchd-rpc_services.Po \ -@AMDEP_TRUE@ ./$(DEPDIR)/launchd_debugd-launchdebugd.Po \ +@AMDEP_TRUE@ ./$(DEPDIR)/launchd-launchd_core_logic.Po \ +@AMDEP_TRUE@ ./$(DEPDIR)/launchd-launchd_internalServer.Po \ +@AMDEP_TRUE@ ./$(DEPDIR)/launchd-launchd_internalUser.Po \ +@AMDEP_TRUE@ ./$(DEPDIR)/launchd-launchd_mach_ipc.Po \ +@AMDEP_TRUE@ ./$(DEPDIR)/launchd-launchd_unix_ipc.Po \ +@AMDEP_TRUE@ ./$(DEPDIR)/launchd-mpm_replyUser.Po \ +@AMDEP_TRUE@ ./$(DEPDIR)/launchd-notifyServer.Po \ @AMDEP_TRUE@ ./$(DEPDIR)/launchproxy.Po ./$(DEPDIR)/liblaunch.Po \ +@AMDEP_TRUE@ ./$(DEPDIR)/liblaunch_profile_a-bootstrap_public.Po \ @AMDEP_TRUE@ ./$(DEPDIR)/liblaunch_profile_a-liblaunch.Po \ -@AMDEP_TRUE@ ./$(DEPDIR)/register_mach_bootstrap_servers.Po \ @AMDEP_TRUE@ ./$(DEPDIR)/wait4path.Po COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) @@ -221,16 +207,15 @@ CCLD = $(CC) LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ CFLAGS = @CFLAGS@ DIST_SOURCES = $(liblaunch_a_SOURCES) $(liblaunch_profile_a_SOURCES) \ - ConsoleMessage.c StartupItemContext.c $(SystemStarter_SOURCES) \ - launchctl.c $(launchd_SOURCES) $(launchd_debugd_SOURCES) \ - launchproxy.c register_mach_bootstrap_servers.c wait4path.c + $(SystemStarter_SOURCES) launchctl.c $(launchd_SOURCES) \ + launchproxy.c wait4path.c NROFF = nroff MANS = $(man1_MANS) $(man5_MANS) $(man8_MANS) DATA = $(sysconf_DATA) DIST_COMMON = Makefile.am Makefile.in config.h.in -SOURCES = $(liblaunch_a_SOURCES) $(liblaunch_profile_a_SOURCES) ConsoleMessage.c StartupItemContext.c $(SystemStarter_SOURCES) launchctl.c $(launchd_SOURCES) $(launchd_debugd_SOURCES) launchproxy.c register_mach_bootstrap_servers.c wait4path.c +SOURCES = $(liblaunch_a_SOURCES) $(liblaunch_profile_a_SOURCES) $(SystemStarter_SOURCES) launchctl.c $(launchd_SOURCES) launchproxy.c wait4path.c all: config.h $(MAKE) $(AM_MAKEFLAGS) all-am @@ -269,6 +254,7 @@ liblaunch.a: $(liblaunch_a_OBJECTS) $(liblaunch_a_DEPENDENCIES) $(liblaunch_a_AR) liblaunch.a $(liblaunch_a_OBJECTS) $(liblaunch_a_LIBADD) $(RANLIB) liblaunch.a liblaunch_profile_a-liblaunch.$(OBJEXT): liblaunch.c +liblaunch_profile_a-bootstrap_public.$(OBJEXT): bootstrap_public.c liblaunch_profile.a: $(liblaunch_profile_a_OBJECTS) $(liblaunch_profile_a_DEPENDENCIES) -rm -f liblaunch_profile.a $(liblaunch_profile_a_AR) liblaunch_profile.a $(liblaunch_profile_a_OBJECTS) $(liblaunch_profile_a_LIBADD) @@ -345,40 +331,32 @@ uninstall-sbinPROGRAMS: clean-sbinPROGRAMS: -test -z "$(sbin_PROGRAMS)" || rm -f $(sbin_PROGRAMS) -ConsoleMessage$(EXEEXT): $(ConsoleMessage_OBJECTS) $(ConsoleMessage_DEPENDENCIES) - @rm -f ConsoleMessage$(EXEEXT) - $(LINK) $(ConsoleMessage_LDFLAGS) $(ConsoleMessage_OBJECTS) $(ConsoleMessage_LDADD) $(LIBS) -StartupItemContext$(EXEEXT): $(StartupItemContext_OBJECTS) $(StartupItemContext_DEPENDENCIES) - @rm -f StartupItemContext$(EXEEXT) - $(LINK) $(StartupItemContext_LDFLAGS) $(StartupItemContext_OBJECTS) $(StartupItemContext_LDADD) $(LIBS) SystemStarter-StartupItems.$(OBJEXT): StartupItems.c SystemStarter-IPC.$(OBJEXT): IPC.c SystemStarter-SystemStarter.$(OBJEXT): SystemStarter.c SystemStarter$(EXEEXT): $(SystemStarter_OBJECTS) $(SystemStarter_DEPENDENCIES) @rm -f SystemStarter$(EXEEXT) $(LINK) $(SystemStarter_LDFLAGS) $(SystemStarter_OBJECTS) $(SystemStarter_LDADD) $(LIBS) +launchctl-launchctl.$(OBJEXT): launchctl.c launchctl$(EXEEXT): $(launchctl_OBJECTS) $(launchctl_DEPENDENCIES) @rm -f launchctl$(EXEEXT) $(LINK) $(launchctl_LDFLAGS) $(launchctl_OBJECTS) $(launchctl_LDADD) $(LIBS) launchd-launchd.$(OBJEXT): launchd.c +launchd-launchd_core_logic.$(OBJEXT): launchd_core_logic.c +launchd-launchd_unix_ipc.$(OBJEXT): launchd_unix_ipc.c +launchd-launchd_mach_ipc.$(OBJEXT): launchd_mach_ipc.c launchd-init.$(OBJEXT): init.c -launchd-bootstrap.$(OBJEXT): bootstrap.c -launchd-lists.$(OBJEXT): lists.c -launchd-rpc_services.$(OBJEXT): rpc_services.c launchd-bootstrapServer.$(OBJEXT): bootstrapServer.c +launchd-notifyServer.$(OBJEXT): notifyServer.c +launchd-launchd_internalUser.$(OBJEXT): launchd_internalUser.c +launchd-launchd_internalServer.$(OBJEXT): launchd_internalServer.c +launchd-mpm_replyUser.$(OBJEXT): mpm_replyUser.c launchd$(EXEEXT): $(launchd_OBJECTS) $(launchd_DEPENDENCIES) @rm -f launchd$(EXEEXT) $(LINK) $(launchd_LDFLAGS) $(launchd_OBJECTS) $(launchd_LDADD) $(LIBS) -launchd_debugd-launchdebugd.$(OBJEXT): launchdebugd.c -launchd_debugd$(EXEEXT): $(launchd_debugd_OBJECTS) $(launchd_debugd_DEPENDENCIES) - @rm -f launchd_debugd$(EXEEXT) - $(LINK) $(launchd_debugd_LDFLAGS) $(launchd_debugd_OBJECTS) $(launchd_debugd_LDADD) $(LIBS) launchproxy$(EXEEXT): $(launchproxy_OBJECTS) $(launchproxy_DEPENDENCIES) @rm -f launchproxy$(EXEEXT) $(LINK) $(launchproxy_LDFLAGS) $(launchproxy_OBJECTS) $(launchproxy_LDADD) $(LIBS) -register_mach_bootstrap_servers$(EXEEXT): $(register_mach_bootstrap_servers_OBJECTS) $(register_mach_bootstrap_servers_DEPENDENCIES) - @rm -f register_mach_bootstrap_servers$(EXEEXT) - $(LINK) $(register_mach_bootstrap_servers_LDFLAGS) $(register_mach_bootstrap_servers_OBJECTS) $(register_mach_bootstrap_servers_LDADD) $(LIBS) wait4path$(EXEEXT): $(wait4path_OBJECTS) $(wait4path_DEPENDENCIES) @rm -f wait4path$(EXEEXT) $(LINK) $(wait4path_LDFLAGS) $(wait4path_OBJECTS) $(wait4path_LDADD) $(LIBS) @@ -409,23 +387,25 @@ mostlyclean-compile: distclean-compile: -rm -f *.tab.c -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ConsoleMessage.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/StartupItemContext.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SystemStarter-IPC.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SystemStarter-StartupItems.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SystemStarter-SystemStarter.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/launchctl.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/launchd-bootstrap.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bootstrap_public.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/launchctl-launchctl.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/launchd-bootstrapServer.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/launchd-init.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/launchd-launchd.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/launchd-lists.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/launchd-rpc_services.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/launchd_debugd-launchdebugd.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/launchd-launchd_core_logic.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/launchd-launchd_internalServer.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/launchd-launchd_internalUser.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/launchd-launchd_mach_ipc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/launchd-launchd_unix_ipc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/launchd-mpm_replyUser.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/launchd-notifyServer.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/launchproxy.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblaunch.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblaunch_profile_a-bootstrap_public.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblaunch_profile_a-liblaunch.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/register_mach_bootstrap_servers.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wait4path.Po@am__quote@ distclean-depend: @@ -455,6 +435,18 @@ liblaunch_profile_a-liblaunch.obj: liblaunch.c @AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblaunch_profile_a_CFLAGS) $(CFLAGS) -c -o liblaunch_profile_a-liblaunch.obj `cygpath -w liblaunch.c` +liblaunch_profile_a-bootstrap_public.o: bootstrap_public.c +@AMDEP_TRUE@ source='bootstrap_public.c' object='liblaunch_profile_a-bootstrap_public.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/liblaunch_profile_a-bootstrap_public.Po' tmpdepfile='$(DEPDIR)/liblaunch_profile_a-bootstrap_public.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblaunch_profile_a_CFLAGS) $(CFLAGS) -c -o liblaunch_profile_a-bootstrap_public.o `test -f 'bootstrap_public.c' || echo '$(srcdir)/'`bootstrap_public.c + +liblaunch_profile_a-bootstrap_public.obj: bootstrap_public.c +@AMDEP_TRUE@ source='bootstrap_public.c' object='liblaunch_profile_a-bootstrap_public.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/liblaunch_profile_a-bootstrap_public.Po' tmpdepfile='$(DEPDIR)/liblaunch_profile_a-bootstrap_public.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblaunch_profile_a_CFLAGS) $(CFLAGS) -c -o liblaunch_profile_a-bootstrap_public.obj `cygpath -w bootstrap_public.c` + SystemStarter-StartupItems.o: StartupItems.c @AMDEP_TRUE@ source='StartupItems.c' object='SystemStarter-StartupItems.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@ depfile='$(DEPDIR)/SystemStarter-StartupItems.Po' tmpdepfile='$(DEPDIR)/SystemStarter-StartupItems.TPo' @AMDEPBACKSLASH@ @@ -491,6 +483,18 @@ SystemStarter-SystemStarter.obj: SystemStarter.c @AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(SystemStarter_CFLAGS) $(CFLAGS) -c -o SystemStarter-SystemStarter.obj `cygpath -w SystemStarter.c` +launchctl-launchctl.o: launchctl.c +@AMDEP_TRUE@ source='launchctl.c' object='launchctl-launchctl.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/launchctl-launchctl.Po' tmpdepfile='$(DEPDIR)/launchctl-launchctl.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(launchctl_CFLAGS) $(CFLAGS) -c -o launchctl-launchctl.o `test -f 'launchctl.c' || echo '$(srcdir)/'`launchctl.c + +launchctl-launchctl.obj: launchctl.c +@AMDEP_TRUE@ source='launchctl.c' object='launchctl-launchctl.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/launchctl-launchctl.Po' tmpdepfile='$(DEPDIR)/launchctl-launchctl.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(launchctl_CFLAGS) $(CFLAGS) -c -o launchctl-launchctl.obj `cygpath -w launchctl.c` + launchd-launchd.o: launchd.c @AMDEP_TRUE@ source='launchd.c' object='launchd-launchd.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@ depfile='$(DEPDIR)/launchd-launchd.Po' tmpdepfile='$(DEPDIR)/launchd-launchd.TPo' @AMDEPBACKSLASH@ @@ -503,53 +507,53 @@ launchd-launchd.obj: launchd.c @AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(launchd_CFLAGS) $(CFLAGS) -c -o launchd-launchd.obj `cygpath -w launchd.c` -launchd-init.o: init.c -@AMDEP_TRUE@ source='init.c' object='launchd-init.o' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@ depfile='$(DEPDIR)/launchd-init.Po' tmpdepfile='$(DEPDIR)/launchd-init.TPo' @AMDEPBACKSLASH@ +launchd-launchd_core_logic.o: launchd_core_logic.c +@AMDEP_TRUE@ source='launchd_core_logic.c' object='launchd-launchd_core_logic.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/launchd-launchd_core_logic.Po' tmpdepfile='$(DEPDIR)/launchd-launchd_core_logic.TPo' @AMDEPBACKSLASH@ @AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ - $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(launchd_CFLAGS) $(CFLAGS) -c -o launchd-init.o `test -f 'init.c' || echo '$(srcdir)/'`init.c + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(launchd_CFLAGS) $(CFLAGS) -c -o launchd-launchd_core_logic.o `test -f 'launchd_core_logic.c' || echo '$(srcdir)/'`launchd_core_logic.c -launchd-init.obj: init.c -@AMDEP_TRUE@ source='init.c' object='launchd-init.obj' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@ depfile='$(DEPDIR)/launchd-init.Po' tmpdepfile='$(DEPDIR)/launchd-init.TPo' @AMDEPBACKSLASH@ +launchd-launchd_core_logic.obj: launchd_core_logic.c +@AMDEP_TRUE@ source='launchd_core_logic.c' object='launchd-launchd_core_logic.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/launchd-launchd_core_logic.Po' tmpdepfile='$(DEPDIR)/launchd-launchd_core_logic.TPo' @AMDEPBACKSLASH@ @AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ - $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(launchd_CFLAGS) $(CFLAGS) -c -o launchd-init.obj `cygpath -w init.c` + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(launchd_CFLAGS) $(CFLAGS) -c -o launchd-launchd_core_logic.obj `cygpath -w launchd_core_logic.c` -launchd-bootstrap.o: bootstrap.c -@AMDEP_TRUE@ source='bootstrap.c' object='launchd-bootstrap.o' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@ depfile='$(DEPDIR)/launchd-bootstrap.Po' tmpdepfile='$(DEPDIR)/launchd-bootstrap.TPo' @AMDEPBACKSLASH@ +launchd-launchd_unix_ipc.o: launchd_unix_ipc.c +@AMDEP_TRUE@ source='launchd_unix_ipc.c' object='launchd-launchd_unix_ipc.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/launchd-launchd_unix_ipc.Po' tmpdepfile='$(DEPDIR)/launchd-launchd_unix_ipc.TPo' @AMDEPBACKSLASH@ @AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ - $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(launchd_CFLAGS) $(CFLAGS) -c -o launchd-bootstrap.o `test -f 'bootstrap.c' || echo '$(srcdir)/'`bootstrap.c + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(launchd_CFLAGS) $(CFLAGS) -c -o launchd-launchd_unix_ipc.o `test -f 'launchd_unix_ipc.c' || echo '$(srcdir)/'`launchd_unix_ipc.c -launchd-bootstrap.obj: bootstrap.c -@AMDEP_TRUE@ source='bootstrap.c' object='launchd-bootstrap.obj' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@ depfile='$(DEPDIR)/launchd-bootstrap.Po' tmpdepfile='$(DEPDIR)/launchd-bootstrap.TPo' @AMDEPBACKSLASH@ +launchd-launchd_unix_ipc.obj: launchd_unix_ipc.c +@AMDEP_TRUE@ source='launchd_unix_ipc.c' object='launchd-launchd_unix_ipc.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/launchd-launchd_unix_ipc.Po' tmpdepfile='$(DEPDIR)/launchd-launchd_unix_ipc.TPo' @AMDEPBACKSLASH@ @AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ - $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(launchd_CFLAGS) $(CFLAGS) -c -o launchd-bootstrap.obj `cygpath -w bootstrap.c` + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(launchd_CFLAGS) $(CFLAGS) -c -o launchd-launchd_unix_ipc.obj `cygpath -w launchd_unix_ipc.c` -launchd-lists.o: lists.c -@AMDEP_TRUE@ source='lists.c' object='launchd-lists.o' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@ depfile='$(DEPDIR)/launchd-lists.Po' tmpdepfile='$(DEPDIR)/launchd-lists.TPo' @AMDEPBACKSLASH@ +launchd-launchd_mach_ipc.o: launchd_mach_ipc.c +@AMDEP_TRUE@ source='launchd_mach_ipc.c' object='launchd-launchd_mach_ipc.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/launchd-launchd_mach_ipc.Po' tmpdepfile='$(DEPDIR)/launchd-launchd_mach_ipc.TPo' @AMDEPBACKSLASH@ @AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ - $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(launchd_CFLAGS) $(CFLAGS) -c -o launchd-lists.o `test -f 'lists.c' || echo '$(srcdir)/'`lists.c + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(launchd_CFLAGS) $(CFLAGS) -c -o launchd-launchd_mach_ipc.o `test -f 'launchd_mach_ipc.c' || echo '$(srcdir)/'`launchd_mach_ipc.c -launchd-lists.obj: lists.c -@AMDEP_TRUE@ source='lists.c' object='launchd-lists.obj' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@ depfile='$(DEPDIR)/launchd-lists.Po' tmpdepfile='$(DEPDIR)/launchd-lists.TPo' @AMDEPBACKSLASH@ +launchd-launchd_mach_ipc.obj: launchd_mach_ipc.c +@AMDEP_TRUE@ source='launchd_mach_ipc.c' object='launchd-launchd_mach_ipc.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/launchd-launchd_mach_ipc.Po' tmpdepfile='$(DEPDIR)/launchd-launchd_mach_ipc.TPo' @AMDEPBACKSLASH@ @AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ - $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(launchd_CFLAGS) $(CFLAGS) -c -o launchd-lists.obj `cygpath -w lists.c` + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(launchd_CFLAGS) $(CFLAGS) -c -o launchd-launchd_mach_ipc.obj `cygpath -w launchd_mach_ipc.c` -launchd-rpc_services.o: rpc_services.c -@AMDEP_TRUE@ source='rpc_services.c' object='launchd-rpc_services.o' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@ depfile='$(DEPDIR)/launchd-rpc_services.Po' tmpdepfile='$(DEPDIR)/launchd-rpc_services.TPo' @AMDEPBACKSLASH@ +launchd-init.o: init.c +@AMDEP_TRUE@ source='init.c' object='launchd-init.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/launchd-init.Po' tmpdepfile='$(DEPDIR)/launchd-init.TPo' @AMDEPBACKSLASH@ @AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ - $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(launchd_CFLAGS) $(CFLAGS) -c -o launchd-rpc_services.o `test -f 'rpc_services.c' || echo '$(srcdir)/'`rpc_services.c + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(launchd_CFLAGS) $(CFLAGS) -c -o launchd-init.o `test -f 'init.c' || echo '$(srcdir)/'`init.c -launchd-rpc_services.obj: rpc_services.c -@AMDEP_TRUE@ source='rpc_services.c' object='launchd-rpc_services.obj' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@ depfile='$(DEPDIR)/launchd-rpc_services.Po' tmpdepfile='$(DEPDIR)/launchd-rpc_services.TPo' @AMDEPBACKSLASH@ +launchd-init.obj: init.c +@AMDEP_TRUE@ source='init.c' object='launchd-init.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/launchd-init.Po' tmpdepfile='$(DEPDIR)/launchd-init.TPo' @AMDEPBACKSLASH@ @AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ - $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(launchd_CFLAGS) $(CFLAGS) -c -o launchd-rpc_services.obj `cygpath -w rpc_services.c` + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(launchd_CFLAGS) $(CFLAGS) -c -o launchd-init.obj `cygpath -w init.c` launchd-bootstrapServer.o: bootstrapServer.c @AMDEP_TRUE@ source='bootstrapServer.c' object='launchd-bootstrapServer.o' libtool=no @AMDEPBACKSLASH@ @@ -563,17 +567,53 @@ launchd-bootstrapServer.obj: bootstrapServer.c @AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(launchd_CFLAGS) $(CFLAGS) -c -o launchd-bootstrapServer.obj `cygpath -w bootstrapServer.c` -launchd_debugd-launchdebugd.o: launchdebugd.c -@AMDEP_TRUE@ source='launchdebugd.c' object='launchd_debugd-launchdebugd.o' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@ depfile='$(DEPDIR)/launchd_debugd-launchdebugd.Po' tmpdepfile='$(DEPDIR)/launchd_debugd-launchdebugd.TPo' @AMDEPBACKSLASH@ +launchd-notifyServer.o: notifyServer.c +@AMDEP_TRUE@ source='notifyServer.c' object='launchd-notifyServer.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/launchd-notifyServer.Po' tmpdepfile='$(DEPDIR)/launchd-notifyServer.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(launchd_CFLAGS) $(CFLAGS) -c -o launchd-notifyServer.o `test -f 'notifyServer.c' || echo '$(srcdir)/'`notifyServer.c + +launchd-notifyServer.obj: notifyServer.c +@AMDEP_TRUE@ source='notifyServer.c' object='launchd-notifyServer.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/launchd-notifyServer.Po' tmpdepfile='$(DEPDIR)/launchd-notifyServer.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(launchd_CFLAGS) $(CFLAGS) -c -o launchd-notifyServer.obj `cygpath -w notifyServer.c` + +launchd-launchd_internalUser.o: launchd_internalUser.c +@AMDEP_TRUE@ source='launchd_internalUser.c' object='launchd-launchd_internalUser.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/launchd-launchd_internalUser.Po' tmpdepfile='$(DEPDIR)/launchd-launchd_internalUser.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(launchd_CFLAGS) $(CFLAGS) -c -o launchd-launchd_internalUser.o `test -f 'launchd_internalUser.c' || echo '$(srcdir)/'`launchd_internalUser.c + +launchd-launchd_internalUser.obj: launchd_internalUser.c +@AMDEP_TRUE@ source='launchd_internalUser.c' object='launchd-launchd_internalUser.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/launchd-launchd_internalUser.Po' tmpdepfile='$(DEPDIR)/launchd-launchd_internalUser.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(launchd_CFLAGS) $(CFLAGS) -c -o launchd-launchd_internalUser.obj `cygpath -w launchd_internalUser.c` + +launchd-launchd_internalServer.o: launchd_internalServer.c +@AMDEP_TRUE@ source='launchd_internalServer.c' object='launchd-launchd_internalServer.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/launchd-launchd_internalServer.Po' tmpdepfile='$(DEPDIR)/launchd-launchd_internalServer.TPo' @AMDEPBACKSLASH@ @AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ - $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(launchd_debugd_CFLAGS) $(CFLAGS) -c -o launchd_debugd-launchdebugd.o `test -f 'launchdebugd.c' || echo '$(srcdir)/'`launchdebugd.c + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(launchd_CFLAGS) $(CFLAGS) -c -o launchd-launchd_internalServer.o `test -f 'launchd_internalServer.c' || echo '$(srcdir)/'`launchd_internalServer.c -launchd_debugd-launchdebugd.obj: launchdebugd.c -@AMDEP_TRUE@ source='launchdebugd.c' object='launchd_debugd-launchdebugd.obj' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@ depfile='$(DEPDIR)/launchd_debugd-launchdebugd.Po' tmpdepfile='$(DEPDIR)/launchd_debugd-launchdebugd.TPo' @AMDEPBACKSLASH@ +launchd-launchd_internalServer.obj: launchd_internalServer.c +@AMDEP_TRUE@ source='launchd_internalServer.c' object='launchd-launchd_internalServer.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/launchd-launchd_internalServer.Po' tmpdepfile='$(DEPDIR)/launchd-launchd_internalServer.TPo' @AMDEPBACKSLASH@ @AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ - $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(launchd_debugd_CFLAGS) $(CFLAGS) -c -o launchd_debugd-launchdebugd.obj `cygpath -w launchdebugd.c` + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(launchd_CFLAGS) $(CFLAGS) -c -o launchd-launchd_internalServer.obj `cygpath -w launchd_internalServer.c` + +launchd-mpm_replyUser.o: mpm_replyUser.c +@AMDEP_TRUE@ source='mpm_replyUser.c' object='launchd-mpm_replyUser.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/launchd-mpm_replyUser.Po' tmpdepfile='$(DEPDIR)/launchd-mpm_replyUser.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(launchd_CFLAGS) $(CFLAGS) -c -o launchd-mpm_replyUser.o `test -f 'mpm_replyUser.c' || echo '$(srcdir)/'`mpm_replyUser.c + +launchd-mpm_replyUser.obj: mpm_replyUser.c +@AMDEP_TRUE@ source='mpm_replyUser.c' object='launchd-mpm_replyUser.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/launchd-mpm_replyUser.Po' tmpdepfile='$(DEPDIR)/launchd-mpm_replyUser.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(launchd_CFLAGS) $(CFLAGS) -c -o launchd-mpm_replyUser.obj `cygpath -w mpm_replyUser.c` CCDEPMODE = @CCDEPMODE@ uninstall-info-am: @@ -842,8 +882,6 @@ install-data-am: install-man install-exec-am: install-binPROGRAMS install-libexecPROGRAMS \ install-sbinPROGRAMS install-sbinSCRIPTS install-sysconfDATA - @$(NORMAL_INSTALL) - $(MAKE) $(AM_MAKEFLAGS) install-exec-hook install-info: install-info-am @@ -883,37 +921,53 @@ uninstall-man: uninstall-man1 uninstall-man5 uninstall-man8 uninstall-sbinSCRIPTS uninstall-sysconfDATA -bootstrap.c:: bootstrap.h - -bootstrap.h bootstrapServer.c bootstrapUser.c: $(srcdir)/bootstrap.defs - mig $(MIGFLAGS) $(srcdir)/bootstrap.defs - -$(addprefix $(DESTDIR)/System/Library/StartupItems/, $(STARTUPITEMS)): - mkdir -p $@ - cp $(srcdir)/StartupItems/$(notdir $@) $@ - chmod 755 $@/$(notdir $@) - cp $(srcdir)/StartupItems/$(notdir $@).plist $@/StartupParameters.plist - -install-startupitems: $(addprefix $(DESTDIR)/System/Library/StartupItems/, $(STARTUPITEMS)) - -install-data-hook: install-startupitems - mkdir -p $(DESTDIR)/usr/local/lib/system - cp liblaunch.a $(DESTDIR)/usr/local/lib/system - cp liblaunch.a $(DESTDIR)/usr/local/lib/system/liblaunch_debug.a - cp liblaunch_profile.a $(DESTDIR)/usr/local/lib/system/liblaunch_profile.a - mkdir -p $(DESTDIR)/usr/include - cp $(srcdir)/launch.h $(DESTDIR)/usr/include - mkdir -p $(DESTDIR)/usr/local/include - cp $(srcdir)/launch_priv.h $(DESTDIR)/usr/local/include - mkdir -p $(DESTDIR)/$(sysconfdir)/mach_init.d - mkdir -p $(DESTDIR)/$(sysconfdir)/mach_init_per_user.d - mkdir -p $(DESTDIR)/Library/LaunchDaemons - mkdir -p $(DESTDIR)/Library/LaunchAgents - mkdir -p $(DESTDIR)/System/Library/LaunchDaemons - mkdir -p $(DESTDIR)/System/Library/LaunchAgents - -install-exec-hook: - chmod u+s $(DESTDIR)/$(sbindir)/launchd +bootstrap.h bootstrapServer.c bootstrapUser.c bootstrapServer.h: $(srcdir)/bootstrap.defs + mig $(MIGFLAGS) -sheader bootstrapServer.h $(srcdir)/bootstrap.defs + +@LIBS_ONLY_TRUE@bootstrap_public.c:: bootstrap.h + +@LIBS_ONLY_TRUE@install-data-hook: +@LIBS_ONLY_TRUE@ mkdir -p $(DESTDIR)/usr/local/lib/system +@LIBS_ONLY_TRUE@ cp liblaunch.a $(DESTDIR)/usr/local/lib/system +@LIBS_ONLY_TRUE@ cp liblaunch.a $(DESTDIR)/usr/local/lib/system/liblaunch_debug.a +@LIBS_ONLY_TRUE@ cp liblaunch_profile.a $(DESTDIR)/usr/local/lib/system/liblaunch_profile.a + +@LIBS_ONLY_FALSE@launchd_mach_ipc.c:: bootstrap.h bootstrapServer.h notifyServer.h launchd_internal.h +@LIBS_ONLY_FALSE@launchd_core_logic.c:: bootstrap.h mpm_reply.h +@LIBS_ONLY_FALSE@launchd.c:: bootstrap.h launchd_internalServer.h launchd_internal.h notifyServer.h bootstrapServer.h + +@LIBS_ONLY_FALSE@notifyServer.c notifyServer.h: /usr/include/mach/notify.defs +@LIBS_ONLY_FALSE@ mig $(MIGFLAGS) -header /dev/null -user /dev/null -sheader notifyServer.h /usr/include/mach/notify.defs + +@LIBS_ONLY_FALSE@mpm_replyUser.c mpm_reply.h: $(srcdir)/mpm_reply.defs +@LIBS_ONLY_FALSE@ mig $(MIGFLAGS) -sheader /dev/null -server /dev/null $(srcdir)/mpm_reply.defs + +@LIBS_ONLY_FALSE@launchd_internal.h launchd_internalServer.c launchd_internalUser.c launchd_internalServer.h: $(srcdir)/launchd_internal.defs +@LIBS_ONLY_FALSE@ mig $(MIGFLAGS) -sheader launchd_internalServer.h $(srcdir)/launchd_internal.defs + +@LIBS_ONLY_FALSE@$(addprefix $(DESTDIR)/System/Library/StartupItems/, $(STARTUPITEMS)): +@LIBS_ONLY_FALSE@ mkdir -p $@ +@LIBS_ONLY_FALSE@ cp $(srcdir)/StartupItems/$(notdir $@) $@ +@LIBS_ONLY_FALSE@ chmod 755 $@/$(notdir $@) +@LIBS_ONLY_FALSE@ cp $(srcdir)/StartupItems/$(notdir $@).plist $@/StartupParameters.plist + +@LIBS_ONLY_FALSE@install-startupitems: $(addprefix $(DESTDIR)/System/Library/StartupItems/, $(STARTUPITEMS)) + +@LIBS_ONLY_FALSE@install-data-hook: install-startupitems +@LIBS_ONLY_FALSE@ mkdir -p $(DESTDIR)/usr/libexec +@LIBS_ONLY_FALSE@ cp $(srcdir)/StartupItemContext $(DESTDIR)/usr/libexec +@LIBS_ONLY_FALSE@ mkdir -p $(DESTDIR)/usr/include/servers +@LIBS_ONLY_FALSE@ cp $(srcdir)/launch.h $(DESTDIR)/usr/include +@LIBS_ONLY_FALSE@ cp $(srcdir)/bootstrap_public.h $(DESTDIR)/usr/include/servers/bootstrap.h +@LIBS_ONLY_FALSE@ cp $(srcdir)/bootstrap_public.h $(DESTDIR)/usr/include/servers/bootstrap_defs.h +@LIBS_ONLY_FALSE@ mkdir -p $(DESTDIR)/usr/local/include +@LIBS_ONLY_FALSE@ cp $(srcdir)/launch_priv.h $(DESTDIR)/usr/local/include +@LIBS_ONLY_FALSE@ mkdir -p $(DESTDIR)/$(sysconfdir)/mach_init.d +@LIBS_ONLY_FALSE@ mkdir -p $(DESTDIR)/$(sysconfdir)/mach_init_per_user.d +@LIBS_ONLY_FALSE@ mkdir -p $(DESTDIR)/Library/LaunchDaemons +@LIBS_ONLY_FALSE@ mkdir -p $(DESTDIR)/Library/LaunchAgents +@LIBS_ONLY_FALSE@ mkdir -p $(DESTDIR)/System/Library/LaunchDaemons +@LIBS_ONLY_FALSE@ mkdir -p $(DESTDIR)/System/Library/LaunchAgents # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: diff --git a/launchd/src/StartupItemContext b/launchd/src/StartupItemContext new file mode 100755 index 0000000..502cd14 --- /dev/null +++ b/launchd/src/StartupItemContext @@ -0,0 +1,5 @@ +#!/bin/sh + +unset LAUNCHD_SOCKET + +exec launchctl bsexec / $@ diff --git a/launchd/src/StartupItemContext.c b/launchd/src/StartupItemContext.c deleted file mode 100644 index 529618b..0000000 --- a/launchd/src/StartupItemContext.c +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (c) 2002 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 -#include -#include -#include -#include -#include - -#include - -int main(int argc, char *argv[]) -{ - mach_port_t root_bootstrap_port; - kern_return_t ret; -#if 0 - task_t init_task; - int err; -#endif - - if (argc < 2) { - fprintf(stderr, "usage: %s executable [args...]\n", argv[0]); - exit(1); - } - - if (geteuid() != 0) { - fprintf(stderr, "%s: permission denied: must be run as root\n", argv[0]); - return(EPERM); - exit(1); - } - -#if 0 - /* get init's task port */ - ret = task_for_pid((mach_task_self)(), 1, &init_task); - if (ret != KERN_SUCCESS) { - fprintf(stderr, "%s: task_for_pid() failed: permission denied\n", - argv[0]); - exit(1); - } - /* extract its bootstrap port, which should be the root */ - ret = task_get_bootstrap_port(init_task, &root_bootstrap_port); - if (ret != KERN_SUCCESS) { - fprintf(stderr, "%s: task_get_bootstrap_port() failed: %s\n", - argv[0], mach_error_string(ret)); - exit(2); - } -#else - /* - * Keep looping, getting out bootstrap's parent until it repeats, then we - * know we are at the root/startupItem context. - */ - { - mach_port_t cur_bootstrap; - - root_bootstrap_port = bootstrap_port; - do { - cur_bootstrap = root_bootstrap_port; - ret = bootstrap_parent(cur_bootstrap, &root_bootstrap_port); - if (ret == BOOTSTRAP_NOT_PRIVILEGED) { - fprintf(stderr, "%s: must be run as root\n", argv[0]); - exit(1); - } - } while (root_bootstrap_port != cur_bootstrap); - } -#endif - - /* set that as our bootstrap port */ - ret = task_set_bootstrap_port(mach_task_self(), root_bootstrap_port); - if (ret != KERN_SUCCESS) { - fprintf(stderr, "%s: task_set_bootstrap_port() failed: %s\n", - argv[0], mach_error_string(ret)); - exit(3); - } - - /* exec the program called for */ - execv(argv[1], &argv[1]); /* should not return */ - fprintf(stderr, "%s: exec failed: %s(%d)\n", argv[0], strerror(errno), errno); - return(4); -} diff --git a/launchd/src/StartupItems.c b/launchd/src/StartupItems.c index 3c95129..7dff051 100644 --- a/launchd/src/StartupItems.c +++ b/launchd/src/StartupItems.c @@ -6,25 +6,21 @@ ** * Copyright (c) 1999-2002 Apple Computer, Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ - * - * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights - * Reserved. This file contains Original Code and/or Modifications of - * Original Code as defined in and that 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. - * - * The 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@ + * @APPLE_APACHE_LICENSE_HEADER_START@ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @APPLE_APACHE_LICENSE_HEADER_END@ **/ #include @@ -49,39 +45,11 @@ #define kRunSuccess CFSTR("success") #define kRunFailure CFSTR("failure") -typedef enum { - kPriorityLast = 1, - kPriorityLate = 2, - kPriorityNone = 3, - kPriorityEarly = 4, - kPriorityFirst = 5, - kPriorityNetwork = 10, - kPriorityLocal = 20, -} Priority; - -static Priority -priorityFromString(CFStringRef aPriority) -{ - if (aPriority) { - if (CFEqual(aPriority, CFSTR("Last"))) - return kPriorityLast; - else if (CFEqual(aPriority, CFSTR("Late"))) - return kPriorityLate; - else if (CFEqual(aPriority, CFSTR("None"))) - return kPriorityNone; - else if (CFEqual(aPriority, CFSTR("Early"))) - return kPriorityEarly; - else if (CFEqual(aPriority, CFSTR("First"))) - return kPriorityFirst; - } - return kPriorityNone; -} - -static const char * -argumentForAction(Action anAction) +static const char *argumentForAction(Action anAction) { switch (anAction) { - case kActionStart:return "start"; + case kActionStart: + return "start"; case kActionStop: return "stop"; case kActionRestart: @@ -98,8 +66,7 @@ argumentForAction(Action anAction) return FALSE; \ } -static int -StartupItemValidate(CFDictionaryRef aConfig) +static int StartupItemValidate(CFDictionaryRef aConfig) { if (aConfig && CFGetTypeID(aConfig) == CFDictionaryGetTypeID()) { checkTypeOfValue(kProvidesKey, CFArrayGetTypeID()); @@ -113,13 +80,12 @@ StartupItemValidate(CFDictionaryRef aConfig) /* * remove item from waiting list */ -void -RemoveItemFromWaitingList(StartupContext aStartupContext, CFMutableDictionaryRef anItem) +void RemoveItemFromWaitingList(StartupContext aStartupContext, CFMutableDictionaryRef anItem) { /* Remove the item from the waiting list. */ if (aStartupContext && anItem && aStartupContext->aWaitingList) { - CFRange aRange = {0, CFArrayGetCount(aStartupContext->aWaitingList)}; - CFIndex anIndex = CFArrayGetFirstIndexOfValue(aStartupContext->aWaitingList, aRange, anItem); + CFRange aRange = { 0, CFArrayGetCount(aStartupContext->aWaitingList) }; + CFIndex anIndex = CFArrayGetFirstIndexOfValue(aStartupContext->aWaitingList, aRange, anItem); if (anIndex >= 0) { CFArrayRemoveValueAtIndex(aStartupContext->aWaitingList, anIndex); @@ -131,8 +97,7 @@ RemoveItemFromWaitingList(StartupContext aStartupContext, CFMutableDictionaryRef * add item to failed list, create list if it doesn't exist * return and fail quietly if it can't create list */ -void -AddItemToFailedList(StartupContext aStartupContext, CFMutableDictionaryRef anItem) +void AddItemToFailedList(StartupContext aStartupContext, CFMutableDictionaryRef anItem) { if (aStartupContext && anItem) { /* create the failed list if it doesn't exist */ @@ -145,24 +110,22 @@ AddItemToFailedList(StartupContext aStartupContext, CFMutableDictionaryRef anIte } } - /** * startupItemListGetMatches returns an array of items which contain the string aService in the key aKey **/ -static CFMutableArrayRef -startupItemListGetMatches(CFArrayRef anItemList, CFStringRef aKey, CFStringRef aService) +static CFMutableArrayRef startupItemListGetMatches(CFArrayRef anItemList, CFStringRef aKey, CFStringRef aService) { CFMutableArrayRef aResult = NULL; if (anItemList && aKey && aService) { - CFIndex anItemCount = CFArrayGetCount(anItemList); - CFIndex anItemIndex = 0; + CFIndex anItemCount = CFArrayGetCount(anItemList); + CFIndex anItemIndex = 0; aResult = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); for (anItemIndex = 0; anItemIndex < anItemCount; ++anItemIndex) { CFMutableDictionaryRef anItem = (CFMutableDictionaryRef) CFArrayGetValueAtIndex(anItemList, anItemIndex); - CFArrayRef aList = CFDictionaryGetValue(anItem, aKey); + CFArrayRef aList = CFDictionaryGetValue(anItem, aKey); if (aList) { if (CFArrayContainsValue(aList, CFRangeMake(0, CFArrayGetCount(aList)), aService) && @@ -175,8 +138,7 @@ startupItemListGetMatches(CFArrayRef anItemList, CFStringRef aKey, CFStringRef a return aResult; } -static void -SpecialCasesStartupItemHandler(CFMutableDictionaryRef aConfig) +static void SpecialCasesStartupItemHandler(CFMutableDictionaryRef aConfig) { static const CFStringRef stubitems[] = { CFSTR("Accounting"), @@ -193,15 +155,17 @@ SpecialCasesStartupItemHandler(CFMutableDictionaryRef aConfig) CFSTR("mDNSResponder"), CFSTR("Cron"), CFSTR("Core Graphics"), + CFSTR("Core Services"), CFSTR("Network"), + CFSTR("TIM"), NULL }; CFMutableArrayRef aList, aNewList; - CFIndex i, aCount; + CFIndex i, aCount; CFStringRef ci, type = kRequiresKey; const CFStringRef *c; -again: + again: aList = (CFMutableArrayRef) CFDictionaryGetValue(aConfig, type); if (aList) { aCount = CFArrayGetCount(aList); @@ -230,18 +194,17 @@ again: goto again; } -CFIndex -StartupItemListCountServices(CFArrayRef anItemList) +CFIndex StartupItemListCountServices(CFArrayRef anItemList) { - CFIndex aResult = 0; + CFIndex aResult = 0; if (anItemList) { - CFIndex anItemCount = CFArrayGetCount(anItemList); - CFIndex anItemIndex = 0; + CFIndex anItemCount = CFArrayGetCount(anItemList); + CFIndex anItemIndex = 0; for (anItemIndex = 0; anItemIndex < anItemCount; ++anItemIndex) { CFDictionaryRef anItem = CFArrayGetValueAtIndex(anItemList, anItemIndex); - CFArrayRef aProvidesList = CFDictionaryGetValue(anItem, kProvidesKey); + CFArrayRef aProvidesList = CFDictionaryGetValue(anItem, kProvidesKey); if (aProvidesList) aResult += CFArrayGetCount(aProvidesList); @@ -250,8 +213,7 @@ StartupItemListCountServices(CFArrayRef anItemList) return aResult; } -static bool -StartupItemSecurityCheck(const char *aPath) +static bool StartupItemSecurityCheck(const char *aPath) { struct stat aStatBuf; bool r = true; @@ -280,23 +242,22 @@ StartupItemSecurityCheck(const char *aPath) } if (r == false) { mkdir(kFixerDir, ACCESSPERMS); - close(open(kFixerPath, O_RDWR|O_CREAT, DEFFILEMODE)); + close(open(kFixerPath, O_RDWR|O_CREAT|O_NOCTTY, DEFFILEMODE)); } return r; } -CFMutableArrayRef -StartupItemListCreateWithMask(NSSearchPathDomainMask aMask) +CFMutableArrayRef StartupItemListCreateWithMask(NSSearchPathDomainMask aMask) { CFMutableArrayRef anItemList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); - char aPath[PATH_MAX]; - CFIndex aDomainIndex = 0; + char aPath[PATH_MAX]; + CFIndex aDomainIndex = 0; NSSearchPathEnumerationState aState = NSStartSearchPathEnumeration(NSLibraryDirectory, aMask); while ((aState = NSGetNextSearchPathEnumeration(aState, aPath))) { - DIR *aDirectory; + DIR *aDirectory; strcpy(aPath + strlen(aPath), kStartupItemsPath); ++aDomainIndex; @@ -305,7 +266,7 @@ StartupItemListCreateWithMask(NSSearchPathDomainMask aMask) continue; if ((aDirectory = opendir(aPath))) { - struct dirent *aBundle; + struct dirent *aBundle; while ((aBundle = readdir(aDirectory))) { struct stat aStatBuf; @@ -320,10 +281,10 @@ StartupItemListCreateWithMask(NSSearchPathDomainMask aMask) syslog(LOG_DEBUG, "Found item: %s", aBundleName); - sprintf(aBundlePath, "%s/%s", aPath, aBundleName); + sprintf(aBundlePath, "%s/%s", aPath, aBundleName); sprintf(aBundleExecutablePath, "%s/%s", aBundlePath, aBundleName); - sprintf(aConfigFile, "%s/%s", aBundlePath, kParametersFile); - sprintf(aDisabledFile, "%s/%s", aBundlePath, kDisabledFile); + sprintf(aConfigFile, "%s/%s", aBundlePath, kParametersFile); + sprintf(aDisabledFile, "%s/%s", aBundlePath, kDisabledFile); if (lstat(aDisabledFile, &aStatBuf) == 0) { syslog(LOG_NOTICE, "Skipping disabled StartupItem: %s", aBundlePath); @@ -338,20 +299,20 @@ StartupItemListCreateWithMask(NSSearchPathDomainMask aMask) /* Stow away the plist data for each bundle */ { - int aConfigFileDescriptor; + int aConfigFileDescriptor; - if ((aConfigFileDescriptor = open(aConfigFile, O_RDONLY, (mode_t) 0)) != -1) { - struct stat aConfigFileStatBuffer; + if ((aConfigFileDescriptor = open(aConfigFile, O_RDONLY|O_NOCTTY, (mode_t) 0)) != -1) { + struct stat aConfigFileStatBuffer; if (stat(aConfigFile, &aConfigFileStatBuffer) != -1) { - off_t aConfigFileContentsSize = aConfigFileStatBuffer.st_size; - char *aConfigFileContentsBuffer; + off_t aConfigFileContentsSize = aConfigFileStatBuffer.st_size; + char *aConfigFileContentsBuffer; if ((aConfigFileContentsBuffer = mmap((caddr_t) 0, aConfigFileContentsSize, PROT_READ, MAP_FILE | MAP_PRIVATE, aConfigFileDescriptor, (off_t) 0)) != (caddr_t) - 1) { - CFDataRef aConfigData = NULL; + CFDataRef aConfigData = NULL; CFMutableDictionaryRef aConfig = NULL; aConfigData = @@ -362,17 +323,21 @@ StartupItemListCreateWithMask(NSSearchPathDomainMask aMask) if (aConfigData) { aConfig = (CFMutableDictionaryRef) - CFPropertyListCreateFromXMLData(NULL, aConfigData, - kCFPropertyListMutableContainers, NULL); + CFPropertyListCreateFromXMLData(NULL, aConfigData, + kCFPropertyListMutableContainers, + NULL); } if (StartupItemValidate(aConfig)) { - CFStringRef aBundlePathString = - CFStringCreateWithCString(NULL, aBundlePath, kCFStringEncodingUTF8); + CFStringRef aBundlePathString = + CFStringCreateWithCString(NULL, aBundlePath, + kCFStringEncodingUTF8); - CFNumberRef aDomainNumber = - CFNumberCreate(NULL, kCFNumberCFIndexType, &aDomainIndex); + CFNumberRef aDomainNumber = + CFNumberCreate(NULL, kCFNumberCFIndexType, + &aDomainIndex); - CFDictionarySetValue(aConfig, kBundlePathKey, aBundlePathString); + CFDictionarySetValue(aConfig, kBundlePathKey, + aBundlePathString); CFDictionarySetValue(aConfig, kDomainKey, aDomainNumber); CFRelease(aDomainNumber); SpecialCasesStartupItemHandler(aConfig); @@ -380,7 +345,8 @@ StartupItemListCreateWithMask(NSSearchPathDomainMask aMask) CFRelease(aBundlePathString); } else { - syslog(LOG_ERR, "Malformatted parameters file: %s", aConfigFile); + syslog(LOG_ERR, "Malformatted parameters file: %s", + aConfigFile); } if (aConfig) @@ -388,21 +354,29 @@ StartupItemListCreateWithMask(NSSearchPathDomainMask aMask) if (aConfigData) CFRelease(aConfigData); - if (munmap(aConfigFileContentsBuffer, aConfigFileContentsSize) == -1) { - syslog(LOG_WARNING, "Unable to unmap parameters file %s for item %s: %m", aConfigFile, aBundleName); + if (munmap(aConfigFileContentsBuffer, aConfigFileContentsSize) == + -1) { + syslog(LOG_WARNING, + "Unable to unmap parameters file %s for item %s: %m", + aConfigFile, aBundleName); } } else { - syslog(LOG_ERR, "Unable to map parameters file %s for item %s: %m", aConfigFile, aBundleName); + syslog(LOG_ERR, + "Unable to map parameters file %s for item %s: %m", + aConfigFile, aBundleName); } } else { - syslog(LOG_ERR, "Unable to stat parameters file %s for item %s: %m", aConfigFile, aBundleName); + syslog(LOG_ERR, "Unable to stat parameters file %s for item %s: %m", + aConfigFile, aBundleName); } if (close(aConfigFileDescriptor) == -1) { - syslog(LOG_ERR, "Unable to close parameters file %s for item %s: %m", aConfigFile, aBundleName); + syslog(LOG_ERR, "Unable to close parameters file %s for item %s: %m", + aConfigFile, aBundleName); } } else { - syslog(LOG_ERR, "Unable to open parameters file %s for item %s: %m", aConfigFile, aBundleName); + syslog(LOG_ERR, "Unable to open parameters file %s for item %s: %m", aConfigFile, + aBundleName); } } } @@ -420,8 +394,7 @@ StartupItemListCreateWithMask(NSSearchPathDomainMask aMask) return anItemList; } -CFMutableDictionaryRef -StartupItemListGetProvider(CFArrayRef anItemList, CFStringRef aService) +CFMutableDictionaryRef StartupItemListGetProvider(CFArrayRef anItemList, CFStringRef aService) { CFMutableDictionaryRef aResult = NULL; CFMutableArrayRef aList = startupItemListGetMatches(anItemList, kProvidesKey, aService); @@ -432,16 +405,15 @@ StartupItemListGetProvider(CFArrayRef anItemList, CFStringRef aService) return aResult; } -CFArrayRef -StartupItemListGetRunning(CFArrayRef anItemList) +CFArrayRef StartupItemListGetRunning(CFArrayRef anItemList) { CFMutableArrayRef aResult = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); if (aResult) { - CFIndex anIndex, aCount = CFArrayGetCount(anItemList); + CFIndex anIndex, aCount = CFArrayGetCount(anItemList); for (anIndex = 0; anIndex < aCount; ++anIndex) { CFDictionaryRef anItem = CFArrayGetValueAtIndex(anItemList, anIndex); if (anItem) { - CFNumberRef aPID = CFDictionaryGetValue(anItem, kPIDKey); + CFNumberRef aPID = CFDictionaryGetValue(anItem, kPIDKey); if (aPID) CFArrayAppendValue(aResult, anItem); } @@ -458,13 +430,10 @@ StartupItemListGetRunning(CFArrayRef anItemList) * If anAction is kActionStop, dependent items are those which provide * any service required by aParentItem. */ -static void -appendDependents(CFMutableArrayRef aDependents, - CFArrayRef anItemList, CFDictionaryRef aParentItem, - Action anAction) +static void appendDependents(CFMutableArrayRef aDependents, CFArrayRef anItemList, CFDictionaryRef aParentItem, Action anAction) { - CFStringRef anInnerKey, anOuterKey; - CFArrayRef anOuterList; + CFStringRef anInnerKey, anOuterKey; + CFArrayRef anOuterList; /* Append the parent item to the list (avoiding duplicates) */ if (!CFArrayContainsValue(aDependents, CFRangeMake(0, CFArrayGetCount(aDependents)), aParentItem)) @@ -490,29 +459,29 @@ appendDependents(CFMutableArrayRef aDependents, anOuterList = CFDictionaryGetValue(aParentItem, anOuterKey); if (anOuterList) { - CFIndex anOuterCount = CFArrayGetCount(anOuterList); - CFIndex anOuterIndex; + CFIndex anOuterCount = CFArrayGetCount(anOuterList); + CFIndex anOuterIndex; for (anOuterIndex = 0; anOuterIndex < anOuterCount; anOuterIndex++) { - CFStringRef anOuterElement = CFArrayGetValueAtIndex(anOuterList, anOuterIndex); - CFIndex anItemCount = CFArrayGetCount(anItemList); - CFIndex anItemIndex; + CFStringRef anOuterElement = CFArrayGetValueAtIndex(anOuterList, anOuterIndex); + CFIndex anItemCount = CFArrayGetCount(anItemList); + CFIndex anItemIndex; for (anItemIndex = 0; anItemIndex < anItemCount; anItemIndex++) { CFDictionaryRef anItem = CFArrayGetValueAtIndex(anItemList, anItemIndex); - CFArrayRef anInnerList = CFDictionaryGetValue(anItem, anInnerKey); + CFArrayRef anInnerList = CFDictionaryGetValue(anItem, anInnerKey); if (anInnerList && - CFArrayContainsValue(anInnerList, CFRangeMake(0, CFArrayGetCount(anInnerList)), anOuterElement) && - !CFArrayContainsValue(aDependents, CFRangeMake(0, CFArrayGetCount(aDependents)), anItem)) + CFArrayContainsValue(anInnerList, CFRangeMake(0, CFArrayGetCount(anInnerList)), + anOuterElement) + && !CFArrayContainsValue(aDependents, CFRangeMake(0, CFArrayGetCount(aDependents)), anItem)) appendDependents(aDependents, anItemList, anItem, anAction); } } } } -CFMutableArrayRef -StartupItemListCreateDependentsList(CFMutableArrayRef anItemList, CFStringRef aService, Action anAction) +CFMutableArrayRef StartupItemListCreateDependentsList(CFMutableArrayRef anItemList, CFStringRef aService, Action anAction) { CFMutableArrayRef aDependents = NULL; CFMutableDictionaryRef anItem = NULL; @@ -545,16 +514,15 @@ StartupItemListCreateDependentsList(CFMutableArrayRef anItemList, CFStringRef aS * countUnmetRequirements counts the number of items in anItemList * which are pending in aStatusDict. **/ -static int -countUnmetRequirements(CFDictionaryRef aStatusDict, CFArrayRef anItemList) +static int countUnmetRequirements(CFDictionaryRef aStatusDict, CFArrayRef anItemList) { - int aCount = 0; - CFIndex anItemCount = CFArrayGetCount(anItemList); - CFIndex anItemIndex; + int aCount = 0; + CFIndex anItemCount = CFArrayGetCount(anItemList); + CFIndex anItemIndex; for (anItemIndex = 0; anItemIndex < anItemCount; anItemIndex++) { - CFStringRef anItem = CFArrayGetValueAtIndex(anItemList, anItemIndex); - CFStringRef aStatus = CFDictionaryGetValue(aStatusDict, anItem); + CFStringRef anItem = CFArrayGetValueAtIndex(anItemList, anItemIndex); + CFStringRef aStatus = CFDictionaryGetValue(aStatusDict, anItem); if (!aStatus || !CFEqual(aStatus, kRunSuccess)) { CF_syslog(LOG_DEBUG, CFSTR("\tFailed requirement/uses: %@"), anItem); @@ -569,16 +537,15 @@ countUnmetRequirements(CFDictionaryRef aStatusDict, CFArrayRef anItemList) * countDependantsPresent counts the number of items in aWaitingList * which depend on items in anItemList. **/ -static int -countDependantsPresent(CFArrayRef aWaitingList, CFArrayRef anItemList, CFStringRef aKey) +static int countDependantsPresent(CFArrayRef aWaitingList, CFArrayRef anItemList, CFStringRef aKey) { - int aCount = 0; - CFIndex anItemCount = CFArrayGetCount(anItemList); - CFIndex anItemIndex; + int aCount = 0; + CFIndex anItemCount = CFArrayGetCount(anItemList); + CFIndex anItemIndex; for (anItemIndex = 0; anItemIndex < anItemCount; anItemIndex++) { - CFStringRef anItem = CFArrayGetValueAtIndex(anItemList, anItemIndex); - CFArrayRef aMatchesList = startupItemListGetMatches(aWaitingList, aKey, anItem); + CFStringRef anItem = CFArrayGetValueAtIndex(anItemList, anItemIndex); + CFArrayRef aMatchesList = startupItemListGetMatches(aWaitingList, aKey, anItem); if (aMatchesList) { aCount = aCount + CFArrayGetCount(aMatchesList); @@ -593,29 +560,28 @@ countDependantsPresent(CFArrayRef aWaitingList, CFArrayRef anItemList, CFStringR * pendingAntecedents returns TRUE if any antecedents of this item * are currently running, have not yet run, or none exist. **/ -static Boolean +static Boolean pendingAntecedents(CFArrayRef aWaitingList, CFDictionaryRef aStatusDict, CFArrayRef anAntecedentList, Action anAction) { - int aPendingFlag = FALSE; + int aPendingFlag = FALSE; - CFIndex anAntecedentCount = CFArrayGetCount(anAntecedentList); - CFIndex anAntecedentIndex; + CFIndex anAntecedentCount = CFArrayGetCount(anAntecedentList); + CFIndex anAntecedentIndex; for (anAntecedentIndex = 0; anAntecedentIndex < anAntecedentCount; ++anAntecedentIndex) { - CFStringRef anAntecedent = CFArrayGetValueAtIndex(anAntecedentList, anAntecedentIndex); - CFStringRef aKey = (anAction == kActionStart) ? kProvidesKey : kUsesKey; - CFArrayRef aMatchesList = startupItemListGetMatches(aWaitingList, aKey, anAntecedent); + CFStringRef anAntecedent = CFArrayGetValueAtIndex(anAntecedentList, anAntecedentIndex); + CFStringRef aKey = (anAction == kActionStart) ? kProvidesKey : kUsesKey; + CFArrayRef aMatchesList = startupItemListGetMatches(aWaitingList, aKey, anAntecedent); if (aMatchesList) { - CFIndex aMatchesListCount = CFArrayGetCount(aMatchesList); - CFIndex aMatchesListIndex; + CFIndex aMatchesListCount = CFArrayGetCount(aMatchesList); + CFIndex aMatchesListIndex; for (aMatchesListIndex = 0; aMatchesListIndex < aMatchesListCount; ++aMatchesListIndex) { CFDictionaryRef anItem = CFArrayGetValueAtIndex(aMatchesList, aMatchesListIndex); if (!anItem || - !CFDictionaryGetValue(anItem, kPIDKey) || - !CFDictionaryGetValue(aStatusDict, anAntecedent)) { + !CFDictionaryGetValue(anItem, kPIDKey) || !CFDictionaryGetValue(aStatusDict, anAntecedent)) { aPendingFlag = TRUE; break; } @@ -634,20 +600,19 @@ pendingAntecedents(CFArrayRef aWaitingList, CFDictionaryRef aStatusDict, CFArray * checkForDuplicates returns TRUE if an item provides the same service as a * pending item, or an item that already succeeded. **/ -static Boolean -checkForDuplicates(CFArrayRef aWaitingList, CFDictionaryRef aStatusDict, CFDictionaryRef anItem) +static Boolean checkForDuplicates(CFArrayRef aWaitingList, CFDictionaryRef aStatusDict, CFDictionaryRef anItem) { - int aDuplicateFlag = FALSE; + int aDuplicateFlag = FALSE; - CFArrayRef aProvidesList = CFDictionaryGetValue(anItem, kProvidesKey); - CFIndex aProvidesCount = aProvidesList ? CFArrayGetCount(aProvidesList) : 0; - CFIndex aProvidesIndex; + CFArrayRef aProvidesList = CFDictionaryGetValue(anItem, kProvidesKey); + CFIndex aProvidesCount = aProvidesList ? CFArrayGetCount(aProvidesList) : 0; + CFIndex aProvidesIndex; for (aProvidesIndex = 0; aProvidesIndex < aProvidesCount; ++aProvidesIndex) { - CFStringRef aProvides = CFArrayGetValueAtIndex(aProvidesList, aProvidesIndex); + CFStringRef aProvides = CFArrayGetValueAtIndex(aProvidesList, aProvidesIndex); /* If the service succeeded, return true. */ - CFStringRef aStatus = CFDictionaryGetValue(aStatusDict, aProvides); + CFStringRef aStatus = CFDictionaryGetValue(aStatusDict, aProvides); if (aStatus && CFEqual(aStatus, kRunSuccess)) { aDuplicateFlag = TRUE; break; @@ -657,10 +622,10 @@ checkForDuplicates(CFArrayRef aWaitingList, CFDictionaryRef aStatusDict, CFDicti * might provide that service. */ else { - CFArrayRef aMatchesList = startupItemListGetMatches(aWaitingList, kProvidesKey, aProvides); + CFArrayRef aMatchesList = startupItemListGetMatches(aWaitingList, kProvidesKey, aProvides); if (aMatchesList) { - CFIndex aMatchesListCount = CFArrayGetCount(aMatchesList); - CFIndex aMatchesListIndex; + CFIndex aMatchesListCount = CFArrayGetCount(aMatchesList); + CFIndex aMatchesListIndex; for (aMatchesListIndex = 0; aMatchesListIndex < aMatchesListCount; ++aMatchesListIndex) { CFDictionaryRef anDupItem = CFArrayGetValueAtIndex(aMatchesList, aMatchesListIndex); @@ -672,8 +637,8 @@ checkForDuplicates(CFArrayRef aWaitingList, CFDictionaryRef aStatusDict, CFDicti aDuplicateFlag = TRUE; break; } else { - CFNumberRef anItemDomain = CFDictionaryGetValue(anItem, kDomainKey); - CFNumberRef anotherItemDomain = CFDictionaryGetValue(anDupItem, kDomainKey); + CFNumberRef anItemDomain = CFDictionaryGetValue(anItem, kDomainKey); + CFNumberRef anotherItemDomain = CFDictionaryGetValue(anDupItem, kDomainKey); /* * If anItem was found later * than aDupItem, stall @@ -682,7 +647,8 @@ checkForDuplicates(CFArrayRef aWaitingList, CFDictionaryRef aStatusDict, CFDicti */ if (anItemDomain && anotherItemDomain && - CFNumberCompare(anItemDomain, anotherItemDomain, NULL) == kCFCompareGreaterThan) { + CFNumberCompare(anItemDomain, anotherItemDomain, + NULL) == kCFCompareGreaterThan) { /* * Item not running, * but takes @@ -703,11 +669,12 @@ checkForDuplicates(CFArrayRef aWaitingList, CFDictionaryRef aStatusDict, CFDicti return (aDuplicateFlag); } -CFMutableDictionaryRef -StartupItemListGetNext(CFArrayRef aWaitingList, CFDictionaryRef aStatusDict, Action anAction) +CFMutableDictionaryRef StartupItemListGetNext(CFArrayRef aWaitingList, CFDictionaryRef aStatusDict, Action anAction) { CFMutableDictionaryRef aNextItem = NULL; - CFIndex aWaitingCount = CFArrayGetCount(aWaitingList); + CFIndex aWaitingCount = CFArrayGetCount(aWaitingList); + int aMinFailedAntecedents = INT_MAX; + CFIndex aWaitingIndex; switch (anAction) { case kActionStart: @@ -720,149 +687,123 @@ StartupItemListGetNext(CFArrayRef aWaitingList, CFDictionaryRef aStatusDict, Act return NULL; } - if (aWaitingList && aStatusDict && aWaitingCount > 0) { - Priority aMaxPriority = kPriorityLast; - int aMinFailedAntecedents = INT_MAX; - CFIndex aWaitingIndex; - - /** - * Iterate through the items in aWaitingList and look for an optimally ready item. - **/ - for (aWaitingIndex = 0; aWaitingIndex < aWaitingCount; aWaitingIndex++) { - CFMutableDictionaryRef anItem = (CFMutableDictionaryRef) CFArrayGetValueAtIndex(aWaitingList, aWaitingIndex); - CFArrayRef anAntecedentList; + if (!aWaitingList || !aStatusDict || aWaitingCount <= 0) + return NULL; - /* Filter out running items. */ - if (CFDictionaryGetValue(anItem, kPIDKey)) - goto next_item; + /** + * Iterate through the items in aWaitingList and look for an optimally ready item. + **/ + for (aWaitingIndex = 0; aWaitingIndex < aWaitingCount; aWaitingIndex++) { + CFMutableDictionaryRef anItem = (CFMutableDictionaryRef) CFArrayGetValueAtIndex(aWaitingList, aWaitingIndex); + CFArrayRef anAntecedentList; + int aFailedAntecedentsCount = 0; /* Number of unmet soft + * depenancies */ + Boolean aBestPick = FALSE; /* Is this the best pick + * so far? */ + + /* Filter out running items. */ + if (CFDictionaryGetValue(anItem, kPIDKey)) + continue; - /* - * Filter out dupilicate services; if someone has - * provided what we provide, we don't run. - */ - if (checkForDuplicates(aWaitingList, aStatusDict, anItem)) { - CF_syslog(LOG_DEBUG, CFSTR("Skipping %@ because of duplicate service."), CFDictionaryGetValue(anItem, kDescriptionKey)); - goto next_item; - } - /* - * Dependencies don't matter when restarting an item; - * stop here. - */ - if (anAction == kActionRestart) { - aNextItem = anItem; - break; - } - anAntecedentList = CFDictionaryGetValue(anItem, ((anAction == kActionStart) ? kRequiresKey : kProvidesKey)); + /* + * Filter out dupilicate services; if someone has + * provided what we provide, we don't run. + */ + if (checkForDuplicates(aWaitingList, aStatusDict, anItem)) { + CF_syslog(LOG_DEBUG, CFSTR("Skipping %@ because of duplicate service."), + CFDictionaryGetValue(anItem, kDescriptionKey)); + continue; + } + /* + * Dependencies don't matter when restarting an item; + * stop here. + */ + if (anAction == kActionRestart) { + aNextItem = anItem; + break; + } + anAntecedentList = CFDictionaryGetValue(anItem, ((anAction == kActionStart) ? kRequiresKey : kProvidesKey)); - CF_syslog(LOG_DEBUG, CFSTR("Checking %@"), CFDictionaryGetValue(anItem, kDescriptionKey)); + CF_syslog(LOG_DEBUG, CFSTR("Checking %@"), CFDictionaryGetValue(anItem, kDescriptionKey)); - if (anAntecedentList) - CF_syslog(LOG_DEBUG, CFSTR("Antecedents: %@"), anAntecedentList); - else - syslog(LOG_DEBUG, "No antecedents"); + if (anAntecedentList) + CF_syslog(LOG_DEBUG, CFSTR("Antecedents: %@"), anAntecedentList); + else + syslog(LOG_DEBUG, "No antecedents"); /** * Filter out the items which have unsatisfied antecedents. **/ - if (anAntecedentList && - ((anAction == kActionStart) ? - countUnmetRequirements(aStatusDict, anAntecedentList) : - countDependantsPresent(aWaitingList, anAntecedentList, kRequiresKey))) - goto next_item; + if (anAntecedentList && + ((anAction == kActionStart) ? + countUnmetRequirements(aStatusDict, anAntecedentList) : + countDependantsPresent(aWaitingList, anAntecedentList, kRequiresKey))) + continue; /** * anItem has all hard dependancies met; check for soft dependancies. * We'll favor the item with the fewest unmet soft dependancies here. **/ - { - int aFailedAntecedentsCount = 0; /* Number of unmet soft - * depenancies */ - Boolean aBestPick = FALSE; /* Is this the best pick - * so far? */ + anAntecedentList = CFDictionaryGetValue(anItem, ((anAction == kActionStart) ? kUsesKey : kProvidesKey)); - anAntecedentList = CFDictionaryGetValue(anItem, ((anAction == kActionStart) ? - kUsesKey : kProvidesKey)); + if (anAntecedentList) + CF_syslog(LOG_DEBUG, CFSTR("Soft dependancies: %@"), anAntecedentList); + else + syslog(LOG_DEBUG, "No soft dependancies"); - if (anAntecedentList) - CF_syslog(LOG_DEBUG, CFSTR("Soft dependancies: %@"), anAntecedentList); - else - syslog(LOG_DEBUG, "No soft dependancies"); - - if (anAntecedentList) { - aFailedAntecedentsCount = - ((anAction == kActionStart) ? - countUnmetRequirements(aStatusDict, anAntecedentList) : - countDependantsPresent(aWaitingList, anAntecedentList, kUsesKey)); - } else { - if (aMinFailedAntecedents > 0) - aBestPick = TRUE; - } + if (anAntecedentList) { + aFailedAntecedentsCount = + ((anAction == kActionStart) ? + countUnmetRequirements(aStatusDict, anAntecedentList) : + countDependantsPresent(aWaitingList, anAntecedentList, kUsesKey)); + } else { + if (aMinFailedAntecedents > 0) + aBestPick = TRUE; + } - /* - * anItem has unmet dependencies that will - * likely be met in the future, so delay it - */ - if (aFailedAntecedentsCount > 0 && - pendingAntecedents(aWaitingList, aStatusDict, anAntecedentList, anAction)) { - goto next_item; - } - if (aFailedAntecedentsCount > 0) - syslog(LOG_DEBUG, "Total: %d", aFailedAntecedentsCount); + /* + * anItem has unmet dependencies that will + * likely be met in the future, so delay it + */ + if (aFailedAntecedentsCount > 0 && pendingAntecedents(aWaitingList, aStatusDict, anAntecedentList, anAction)) { + continue; + } + if (aFailedAntecedentsCount > 0) + syslog(LOG_DEBUG, "Total: %d", aFailedAntecedentsCount); - if (aFailedAntecedentsCount > aMinFailedAntecedents) - goto next_item; /* Another item already - * won out */ - if (aFailedAntecedentsCount < aMinFailedAntecedents) - aBestPick = TRUE; + if (aFailedAntecedentsCount > aMinFailedAntecedents) + continue; /* Another item already won out */ - { - Priority aPriority = priorityFromString(CFDictionaryGetValue(anItem, kPriorityKey)); + if (aFailedAntecedentsCount < aMinFailedAntecedents) + aBestPick = TRUE; - if (aBestPick) { - /* - * anItem has less unmet - * dependancies than any - * other item so far, so it - * wins. - */ - syslog(LOG_DEBUG, "Best pick so far, based on failed dependancies (%d->%d)", - aMinFailedAntecedents, aFailedAntecedentsCount); - } else if ((anAction == kActionStart) ? - (aPriority >= aMaxPriority) : - (aPriority <= aMaxPriority)) { - /* - * anItem has a best - * priority, so it wins. - */ - syslog(LOG_DEBUG, "Best pick so far, based on Priority (%d->%d)", - aMaxPriority, aPriority); - } else - goto next_item; /* No soup for you! */ - - /* - * We have a winner! Update success - * parameters to match anItem. - */ - aMinFailedAntecedents = aFailedAntecedentsCount; - aMaxPriority = aPriority; - aNextItem = anItem; - } + if (!aBestPick) + continue; - } /* End of uses section. */ + /* + * anItem has less unmet + * dependancies than any + * other item so far, so it + * wins. + */ + syslog(LOG_DEBUG, "Best pick so far, based on failed dependancies (%d->%d)", + aMinFailedAntecedents, aFailedAntecedentsCount); - next_item: - continue; + /* + * We have a winner! Update success + * parameters to match anItem. + */ + aMinFailedAntecedents = aFailedAntecedentsCount; + aNextItem = anItem; - } /* End of waiting list loop. */ + } /* End of waiting list loop. */ - } /* if (aWaitingList && aWaitingCount > 0) */ return aNextItem; } -CFStringRef -StartupItemGetDescription(CFMutableDictionaryRef anItem) +CFStringRef StartupItemGetDescription(CFMutableDictionaryRef anItem) { - CFStringRef aString = NULL; + CFStringRef aString = NULL; if (anItem) aString = CFDictionaryGetValue(anItem, kDescriptionKey); @@ -871,27 +812,25 @@ StartupItemGetDescription(CFMutableDictionaryRef anItem) return aString; } -pid_t -StartupItemGetPID(CFDictionaryRef anItem) +pid_t StartupItemGetPID(CFDictionaryRef anItem) { - CFIndex anItemPID = 0; - CFNumberRef aPIDNumber = anItem ? CFDictionaryGetValue(anItem, kPIDKey) : NULL; + CFIndex anItemPID = 0; + CFNumberRef aPIDNumber = anItem ? CFDictionaryGetValue(anItem, kPIDKey) : NULL; if (aPIDNumber && CFNumberGetValue(aPIDNumber, kCFNumberCFIndexType, &anItemPID)) return (pid_t) anItemPID; else return 0; } -CFMutableDictionaryRef -StartupItemWithPID(CFArrayRef anItemList, pid_t aPID) +CFMutableDictionaryRef StartupItemWithPID(CFArrayRef anItemList, pid_t aPID) { - CFIndex anItemCount = CFArrayGetCount(anItemList); - CFIndex anItemIndex; + CFIndex anItemCount = CFArrayGetCount(anItemList); + CFIndex anItemIndex; for (anItemIndex = 0; anItemIndex < anItemCount; anItemIndex++) { CFMutableDictionaryRef anItem = (CFMutableDictionaryRef) CFArrayGetValueAtIndex(anItemList, anItemIndex); - CFNumberRef aPIDNumber = CFDictionaryGetValue(anItem, kPIDKey); - CFIndex anItemPID; + CFNumberRef aPIDNumber = CFDictionaryGetValue(anItem, kPIDKey); + CFIndex anItemPID; if (aPIDNumber) { CFNumberGetValue(aPIDNumber, kCFNumberCFIndexType, &anItemPID); @@ -904,8 +843,7 @@ StartupItemWithPID(CFArrayRef anItemList, pid_t aPID) return NULL; } -int -StartupItemRun(CFMutableDictionaryRef aStatusDict, CFMutableDictionaryRef anItem, Action anAction) +int StartupItemRun(CFMutableDictionaryRef aStatusDict, CFMutableDictionaryRef anItem, Action anAction) { int anError = -1; CFArrayRef aProvidesList = CFDictionaryGetValue(anItem, kProvidesKey); @@ -951,11 +889,11 @@ StartupItemRun(CFMutableDictionaryRef aStatusDict, CFMutableDictionaryRef anItem StartupItemExit(aStatusDict, anItem, TRUE); anError = 0; } else { - CFStringRef aBundlePathString = CFDictionaryGetValue(anItem, kBundlePathKey); - size_t aBundlePathCLength = - CFStringGetMaximumSizeForEncoding(CFStringGetLength(aBundlePathString), kCFStringEncodingUTF8) + 1; - char *aBundlePath = (char *) malloc(aBundlePathCLength); - char anExecutable[PATH_MAX] = ""; + CFStringRef aBundlePathString = CFDictionaryGetValue(anItem, kBundlePathKey); + size_t aBundlePathCLength = + CFStringGetMaximumSizeForEncoding(CFStringGetLength(aBundlePathString), kCFStringEncodingUTF8) + 1; + char *aBundlePath = (char *)malloc(aBundlePathCLength); + char anExecutable[PATH_MAX] = ""; if (!aBundlePath) { syslog(LOG_EMERG, "malloc() failed; out of memory while running item %s", aBundlePathString); @@ -984,8 +922,8 @@ StartupItemRun(CFMutableDictionaryRef aStatusDict, CFMutableDictionaryRef anItem * Add PID key so that this item is marked as having * been run. */ - CFIndex aPID = -1; - CFNumberRef aProcessNumber = CFNumberCreate(NULL, kCFNumberCFIndexType, &aPID); + CFIndex aPID = -1; + CFNumberRef aProcessNumber = CFNumberCreate(NULL, kCFNumberCFIndexType, &aPID); CFDictionarySetValue(anItem, kPIDKey, aProcessNumber); CFRelease(aProcessNumber); @@ -994,22 +932,21 @@ StartupItemRun(CFMutableDictionaryRef aStatusDict, CFMutableDictionaryRef anItem StartupItemExit(aStatusDict, anItem, FALSE); syslog(LOG_ERR, "No executable file %s", anExecutable); } else { - pid_t aProccessID = fork(); + pid_t aProccessID = fork(); switch (aProccessID) { case -1: /* SystemStarter (fork failed) */ CFDictionarySetValue(anItem, kErrorKey, kErrorFork); StartupItemExit(aStatusDict, anItem, FALSE); - CF_syslog(LOG_ERR, CFSTR("Failed to fork for item %@: %s"), - aBundlePathString, strerror(errno)); + CF_syslog(LOG_ERR, CFSTR("Failed to fork for item %@: %s"), aBundlePathString, strerror(errno)); break; default: /* SystemStarter (fork succeeded) */ { - CFIndex aPID = (CFIndex) aProccessID; - CFNumberRef aProcessNumber = CFNumberCreate(NULL, kCFNumberCFIndexType, &aPID); + CFIndex aPID = (CFIndex) aProccessID; + CFNumberRef aProcessNumber = CFNumberCreate(NULL, kCFNumberCFIndexType, &aPID); CFDictionarySetValue(anItem, kPIDKey, aProcessNumber); CFRelease(aProcessNumber); @@ -1020,7 +957,7 @@ StartupItemRun(CFMutableDictionaryRef aStatusDict, CFMutableDictionaryRef anItem } break; - case 0:/* Child */ + case 0: /* Child */ { setpriority(PRIO_PROCESS, 0, 0); if (setsid() == -1) @@ -1041,31 +978,32 @@ StartupItemRun(CFMutableDictionaryRef aStatusDict, CFMutableDictionaryRef anItem return (anError); } -void -StartupItemSetStatus(CFMutableDictionaryRef aStatusDict, CFMutableDictionaryRef anItem, CFStringRef aServiceName, Boolean aSuccess, Boolean aReplaceFlag) +void +StartupItemSetStatus(CFMutableDictionaryRef aStatusDict, CFMutableDictionaryRef anItem, CFStringRef aServiceName, + Boolean aSuccess, Boolean aReplaceFlag) { - void (*anAction) (CFMutableDictionaryRef, const void *, const void *) = aReplaceFlag ? - CFDictionarySetValue : CFDictionaryAddValue; + void (*anAction) (CFMutableDictionaryRef, const void *, const void *) = aReplaceFlag ? + CFDictionarySetValue : CFDictionaryAddValue; if (aStatusDict && anItem) { - CFArrayRef aProvidesList = CFDictionaryGetValue(anItem, kProvidesKey); + CFArrayRef aProvidesList = CFDictionaryGetValue(anItem, kProvidesKey); if (aProvidesList) { - CFIndex aProvidesCount = CFArrayGetCount(aProvidesList); - CFIndex aProvidesIndex; + CFIndex aProvidesCount = CFArrayGetCount(aProvidesList); + CFIndex aProvidesIndex; /* * If a service name was specified, and it is valid, * use only it. */ if (aServiceName && CFArrayContainsValue(aProvidesList, CFRangeMake(0, aProvidesCount), aServiceName)) { - aProvidesList = CFArrayCreate(NULL, (const void **) &aServiceName, 1, &kCFTypeArrayCallBacks); + aProvidesList = CFArrayCreate(NULL, (const void **)&aServiceName, 1, &kCFTypeArrayCallBacks); aProvidesCount = 1; } else { CFRetain(aProvidesList); } for (aProvidesIndex = 0; aProvidesIndex < aProvidesCount; aProvidesIndex++) { - CFStringRef aService = CFArrayGetValueAtIndex(aProvidesList, aProvidesIndex); + CFStringRef aService = CFArrayGetValueAtIndex(aProvidesList, aProvidesIndex); if (aSuccess) anAction(aStatusDict, aService, kRunSuccess); @@ -1078,8 +1016,7 @@ StartupItemSetStatus(CFMutableDictionaryRef aStatusDict, CFMutableDictionaryRef } } -void -StartupItemExit(CFMutableDictionaryRef aStatusDict, CFMutableDictionaryRef anItem, Boolean aSuccess) +void StartupItemExit(CFMutableDictionaryRef aStatusDict, CFMutableDictionaryRef anItem, Boolean aSuccess) { StartupItemSetStatus(aStatusDict, anItem, NULL, aSuccess, FALSE); } diff --git a/launchd/src/StartupItems.h b/launchd/src/StartupItems.h index aa9f202..12833df 100644 --- a/launchd/src/StartupItems.h +++ b/launchd/src/StartupItems.h @@ -6,25 +6,21 @@ ** * Copyright (c) 1999-2002 Apple Computer, Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ + * @APPLE_APACHE_LICENSE_HEADER_START@ * - * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights - * Reserved. This file contains Original Code and/or Modifications of - * Original Code as defined in and that 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * The 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. + * http://www.apache.org/licenses/LICENSE-2.0 * - * @APPLE_LICENSE_HEADER_END@ + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @APPLE_APACHE_LICENSE_HEADER_END@ **/ #ifndef _StartupItems_H_ @@ -42,7 +38,6 @@ #define kDescriptionKey CFSTR("Description") #define kUsesKey CFSTR("Uses") #define kErrorKey CFSTR("Error") -#define kPriorityKey CFSTR("OrderPreference") #define kBundlePathKey CFSTR("PathToBundle") #define kPIDKey CFSTR("ProcessID") #define kDomainKey CFSTR("Domain") @@ -72,14 +67,12 @@ CFMutableArrayRef StartupItemListCreateDependentsList (CFMutableArrayRef anItemL Action anAction ); /* - * Given aWaitingList of startup items, and aStatusDict describing the - * current startup state, returns the next startup item to run, if any. - * Returns nil if none is available. - * The startup order depends on the dependancies between items and the - * priorities of the items. - * Note that this is not necessarily deterministic; if more than one - * startup item with the same priority is ready to run, which item gets - * returned is not specified. + * Given aWaitingList of startup items, and aStatusDict describing the current + * startup state, returns the next startup item to run, if any. Returns nil if + * none is available. + * Note that this is not necessarily deterministic; if more than one startup + * item is ready to run, which item gets returned is not specified. An item is + * not ready to run if the specified dependencies are not satisfied yet. */ CFMutableDictionaryRef StartupItemListGetNext (CFArrayRef aWaitingList, CFDictionaryRef aStatusDict , diff --git a/launchd/src/StartupItems/Apache b/launchd/src/StartupItems/Apache deleted file mode 100644 index f84e6a5..0000000 --- a/launchd/src/StartupItems/Apache +++ /dev/null @@ -1,52 +0,0 @@ -#!/bin/sh - -## -# Apache HTTP Server -## - -. /etc/rc.common - -StartService () -{ - if [ "${WEBSERVER:=-NO-}" = "-YES-" ]; then - echo "Starting Apache web server" - if [ ! -e /etc/httpd/httpd.conf ] ; then - cp -p /etc/httpd/httpd.conf.default /etc/httpd/httpd.conf - fi - apachectl start - if [ "${WEBPERFCACHESERVER:=-NO-}" = "-YES-" ]; then - if [ -x /usr/sbin/webperfcachectl ]; then - echo "Starting web performance cache server" - /usr/sbin/webperfcachectl start - fi - fi - fi -} - -StopService () -{ - if [ -x /usr/sbin/webperfcachectl ]; then - echo "Stopping web performance cache server" - /usr/sbin/webperfcachectl stop - fi - echo "Stopping Apache web server" - apachectl stop -} - -RestartService () -{ - if [ "${WEBSERVER:=-NO-}" = "-YES-" ]; then - echo "Restarting Apache web server" - apachectl restart - if [ "${WEBPERFCACHESERVER:=-NO-}" = "-YES-" ]; then - if [ -x /usr/sbin/webperfcachectl ]; then - echo "Restarting web performance cache server" - /usr/sbin/webperfcachectl restart - fi - fi - else - StopService - fi -} - -RunService "$1" diff --git a/launchd/src/StartupItems/Apache.plist b/launchd/src/StartupItems/Apache.plist deleted file mode 100644 index 92fe708..0000000 --- a/launchd/src/StartupItems/Apache.plist +++ /dev/null @@ -1,5 +0,0 @@ -{ - Description = "Apache web server"; - Provides = ("Web Server"); - Uses = ("Disks", "NFS"); -} diff --git a/launchd/src/StartupItems/AppServices b/launchd/src/StartupItems/AppServices deleted file mode 100644 index cb7f2ca..0000000 --- a/launchd/src/StartupItems/AppServices +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/sh - -## -# Application Services -## - -. /etc/rc.common - -StartService () -{ - # Launch Services - if [ -x /System/Library/Frameworks/ApplicationServices.framework/Frameworks/LaunchServices.framework/Versions/Current/Support/lsregister ]; then - /System/Library/Frameworks/ApplicationServices.framework/Frameworks/LaunchServices.framework/Versions/Current/Support/lsregister -load - fi -} - -StopService () -{ - return 0 -} - -RestartService () -{ - return 0 -} - -RunService "$1" diff --git a/launchd/src/StartupItems/AppServices.plist b/launchd/src/StartupItems/AppServices.plist deleted file mode 100644 index 90254aa..0000000 --- a/launchd/src/StartupItems/AppServices.plist +++ /dev/null @@ -1,4 +0,0 @@ -{ - Description = "application services"; - Provides = ("Core Services"); -} diff --git a/launchd/src/StartupItems/AppleShare b/launchd/src/StartupItems/AppleShare index 7d2af69..429e6be 100644 --- a/launchd/src/StartupItems/AppleShare +++ b/launchd/src/StartupItems/AppleShare @@ -4,6 +4,8 @@ # Apple File Protocol ## +[ -f /System/Library/LaunchDaemons/com.apple.AppleFileServer.plist ] && exit 0 + . /etc/rc.common StartService () diff --git a/launchd/src/StartupItems/AuthServer b/launchd/src/StartupItems/AuthServer deleted file mode 100644 index 58d564b..0000000 --- a/launchd/src/StartupItems/AuthServer +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/sh - -## -# Authentication Server -## - -. /etc/rc.common - -StartService () -{ - if [ "${AUTHSERVER:=-NO-}" = "-YES-" ]; then - echo "Starting Authentication Server" - tim - fi -} - -StopService () -{ - if [ "${AUTHSERVER:=-NO-}" = "-YES-" ]; then - echo "Stopping Authentication Server" - fi - killall -TERM tim > /dev/null 2>&1 -} - -RestartService () -{ - StopService - StartService -} - -RunService "$1" diff --git a/launchd/src/StartupItems/AuthServer.plist b/launchd/src/StartupItems/AuthServer.plist deleted file mode 100644 index f88ca20..0000000 --- a/launchd/src/StartupItems/AuthServer.plist +++ /dev/null @@ -1,4 +0,0 @@ -{ - Description = "authentication service"; - Provides = ("TIM"); -} diff --git a/launchd/src/StartupItems/IPServices b/launchd/src/StartupItems/IPServices index bb2ae9a..658aafe 100644 --- a/launchd/src/StartupItems/IPServices +++ b/launchd/src/StartupItems/IPServices @@ -4,6 +4,8 @@ # IP Services ## +[ -f /System/Library/LaunchDaemons/com.apache.httpd.plist ] && exit 0 + . /etc/rc.common StartService () diff --git a/launchd/src/StartupItems/NFS b/launchd/src/StartupItems/NFS deleted file mode 100644 index ac16c5e..0000000 --- a/launchd/src/StartupItems/NFS +++ /dev/null @@ -1,126 +0,0 @@ -#!/bin/sh - -## -# Network File System -## - -. /etc/rc.common - -AUTOMOUNTDIR=/private/var/automount - -StartService () -{ - CheckForNetwork - if [ "${NETWORKUP}" = "-NO-" ]; then exit; fi - lockfile -r 0 /var/run/NFS.StartupItem || exit 0 - - ## - # Set up NFS client. - ## - echo "Starting network file system" - - if [ -d ${AUTOMOUNTDIR} ]; then - chflags -R nouchg ${AUTOMOUNTDIR} - rm -rf ${AUTOMOUNTDIR} - fi - - # nsfiod is the NFS asynchronous block I/O daemon, which implements - # NFS read-ahead and write-behind caching on NFS clients. - nfsiod -n 4 - - ## - # The rpc.lockd/rpc.statd daemons are needed on both the client and - # the server in order to support file locking over NFS. - # - # If NFSLOCKS = -AUTOMATIC-, we start the daemons if we are a server - # but if we are only a client, we start the daemons once we know we - # need them. - ## - - ## - # gather list of NFS exports - ## - exports_ni=$(niutil -list . /exports 2> /dev/null | wc -w) - # Look for exports in /etc/exports, ignoring comments and blank lines. - exports_etc=$(grep -v '^[[:space:]]*\(#\|$\)' /etc/exports 2> /dev/null | wc -l) - exports=$(($exports_ni + $exports_etc)) - - # if we are an NFS server, turn on NFS locking by default: - if [ "${exports}" -gt 0 ]; then - if [ "${NFSLOCKS:=-AUTOMATIC-}" = "-AUTOMATIC-" ]; then - NFSLOCKS=-YES-; - fi - fi - - if [ "${NFSLOCKS:=-AUTOMATIC-}" = "-YES-" ]; then - # we definitely want locks on, so turn them on now - rpc.statd - rpc.lockd - fi - if [ "${NFSLOCKS:=-AUTOMATIC-}" = "-AUTOMATIC-" ]; then - # delay starting daemons until we know we need them - - # invoke rpc.statd to send any SM_NOTIFY messages and quit. - rpc.statd -n - - # -w says to wait for signal from kernel, then start daemons - rpc.lockd -w - fi - - ## - # Set up NFS server. - ## - - # If exportfs finds something to export (either using /etc/exports or the - # exports NetInfo directory), then start the NFS daemons (which service - # NFS requests) and the mount server (which services NFS mount requests). - - # Clear the table of exported filesystems. - rm -f /var/db/mountdtab - - if [ "${exports}" -gt 0 ]; then - echo "Starting Network File System server" - mountd - - # If the NetInfo config/nfsd directory contains startup args for nfsd, use those. - arguments=`niutil -readprop . /config/nfsd arguments` - if [ "${arguments}" = "" ]; then - arguments="-t -u -n 6" - fi - nfsd ${arguments} - fi - - ## - # Start the automounter - ## - - if [ "${AUTOMOUNT:=-YES-}" = "-YES-" ]; then - automount -m /Network -nsl -mnt ${AUTOMOUNTDIR} - ln -s /automount/Library /Network/Library - automount -m /automount/Servers -fstab -mnt /private/Network/Servers \ - -m /automount/static -static -mnt ${AUTOMOUNTDIR} - ln -s /automount/Servers /Network/Servers - - # - # Hint that the name /Network should be localized: - # - ln -s . /Network/.localized - fi - - # - # Leave a mark upon completion of the automounter startup: - # - touch /var/run/automount.initialized -} - -StopService () -{ - return 0 -} - -RestartService () -{ - return 0 -} - -RunService "$1" diff --git a/launchd/src/StartupItems/NFS.plist b/launchd/src/StartupItems/NFS.plist deleted file mode 100644 index c3d949f..0000000 --- a/launchd/src/StartupItems/NFS.plist +++ /dev/null @@ -1,5 +0,0 @@ -{ - Description = "network file system"; - Provides = ("NFS"); - Uses = ("Disks"); -} diff --git a/launchd/src/StartupItems/NetworkTime b/launchd/src/StartupItems/NetworkTime deleted file mode 100644 index b80cfd3..0000000 --- a/launchd/src/StartupItems/NetworkTime +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/sh - -## -# Network Time -## - -. /etc/rc.common - -StartService () -{ - if [ "${TIMESYNC:=-YES-}" = "-YES-" ] && - ! GetPID ntpd > /dev/null; then - - CheckForNetwork - - if [ -f /var/run/NetworkTime.StartupItem -o "${NETWORKUP}" = "-NO-" ]; then exit; fi - touch /var/run/NetworkTime.StartupItem - - - echo "Starting network time synchronization" - - # Synchronize our clock to the network's time, - # then fire off ntpd to keep the clock in sync. - ntpdate -bvs - ntpd -f /var/run/ntp.drift -p /var/run/ntpd.pid - fi -} - -StopService () -{ - if pid=$(GetPID ntpd); then - echo "Stopping network time synchronization" - kill -TERM "${pid}" - else - echo "ntpd is not running." - fi - rm -f /var/run/NetworkTime.StartupItem -} - -RestartService () { StopService; StartService; } - -RunService "$1" diff --git a/launchd/src/StartupItems/NetworkTime.plist b/launchd/src/StartupItems/NetworkTime.plist deleted file mode 100644 index fb5fd19..0000000 --- a/launchd/src/StartupItems/NetworkTime.plist +++ /dev/null @@ -1,4 +0,0 @@ -{ - Description = "network time synchronization"; - Provides = ("Network Time"); -} diff --git a/launchd/src/SystemStarter.c b/launchd/src/SystemStarter.c index ccefdb2..0e8ea54 100644 --- a/launchd/src/SystemStarter.c +++ b/launchd/src/SystemStarter.c @@ -5,25 +5,21 @@ ** * Copyright (c) 1999-2002 Apple Computer, Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ - * - * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights - * Reserved. This file contains Original Code and/or Modifications of - * Original Code as defined in and that 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. - * - * The 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@ + * @APPLE_APACHE_LICENSE_HEADER_START@ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @APPLE_APACHE_LICENSE_HEADER_END@ **/ #include @@ -43,6 +39,7 @@ bool gNoRunFlag = false; static void usage(void) __attribute__((noreturn)); static int system_starter(Action anAction, const char *aService); +static void displayErrorMessages(StartupContext aStartupContext); static void doCFnote(void); int @@ -116,7 +113,6 @@ main(int argc, char *argv[]) int status; setpriority(PRIO_PROCESS, 0, 20); - daemon(0, 0); /* Too many old StartupItems had implicit dependancies on * "Network" via other StartupItems that are now no-ops. diff --git a/launchd/src/SystemStarter.h b/launchd/src/SystemStarter.h index 37fb4d5..6a6c0ab 100644 --- a/launchd/src/SystemStarter.h +++ b/launchd/src/SystemStarter.h @@ -5,25 +5,21 @@ ** * Copyright (c) 1999-2002 Apple Computer, Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ + * @APPLE_APACHE_LICENSE_HEADER_START@ * - * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights - * Reserved. This file contains Original Code and/or Modifications of - * Original Code as defined in and that 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * The 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. + * http://www.apache.org/licenses/LICENSE-2.0 * - * @APPLE_LICENSE_HEADER_END@ + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @APPLE_APACHE_LICENSE_HEADER_END@ **/ #ifndef _SYSTEM_STARTER_H_ diff --git a/launchd/src/SystemStarterIPC.h b/launchd/src/SystemStarterIPC.h index d031f1b..3a94095 100644 --- a/launchd/src/SystemStarterIPC.h +++ b/launchd/src/SystemStarterIPC.h @@ -5,25 +5,21 @@ ** * Copyright (c) 1999-2001 Apple Computer, Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ + * @APPLE_APACHE_LICENSE_HEADER_START@ * - * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights - * Reserved. This file contains Original Code and/or Modifications of - * Original Code as defined in and that 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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * The 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. + * http://www.apache.org/licenses/LICENSE-2.0 * - * @APPLE_LICENSE_HEADER_END@ + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @APPLE_APACHE_LICENSE_HEADER_END@ ** * Definitions used for IPC communications with SystemStarter. * SystemStarter listens on a CFMessagePort with the name defined by diff --git a/launchd/src/bootstrap.c b/launchd/src/bootstrap.c deleted file mode 100644 index 063d4d3..0000000 --- a/launchd/src/bootstrap.c +++ /dev/null @@ -1,1074 +0,0 @@ -/* - * Copyright (c) 1999-2004 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights - * Reserved. This file contains Original Code and/or Modifications of - * Original Code as defined in and that are subject to the Apple Public - * Source License Version 1.0 (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. - * - * The 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@ - */ -/* - * bootstrap -- fundamental service initiator and port server - * Mike DeMoney, NeXT, Inc. - * Copyright, 1990. All rights reserved. - * - * bootstrap.c -- implementation of bootstrap main service loop - */ - -/* - * Imports - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "bootstrap.h" -#include "bootstrap_internal.h" -#include "lists.h" -#include "launchd.h" - -/* Mig should produce a declaration for this, but doesn't */ -extern boolean_t bootstrap_server(mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP); - -auditinfo_t inherited_audit; -mach_port_t inherited_bootstrap_port = MACH_PORT_NULL; -bool forward_ok = false; -bool debugging = false; -bool register_self = false; -const char *register_name = NULL; -task_t bootstrap_self = MACH_PORT_NULL; - -static uid_t inherited_uid = 0; -static bool shutdown_in_progress = false; - -#ifndef ASSERT -#define ASSERT(p) -#endif - -/* - * Private macros - */ -#define NELEM(x) (sizeof(x)/sizeof(x[0])) -#define END_OF(x) (&(x)[NELEM(x)]) -#define streq(a,b) (strcmp(a,b) == 0) - -/* - * Private declarations - */ -static void init_ports(void); -static void start_server(server_t *serverp); -static void exec_server(server_t *serverp); -static char **argvize(const char *string); -static void *demand_loop(void *arg); -void *mach_server_loop(void *); -extern kern_return_t bootstrap_register -( - mach_port_t bootstrapport, - name_t servicename, - mach_port_t serviceport -); - -/* - * Private ports we hold receive rights for. We also hold receive rights - * for all the privileged ports. Those are maintained in the server - * structs. - */ -mach_port_t bootstrap_port_set; -mach_port_t demand_port_set; -pthread_t demand_thread; - -mach_port_t notify_port; -mach_port_t backup_port; - - -static mach_msg_return_t -inform_server_loop( - mach_port_name_t about, - mach_msg_option_t options) -{ - mach_port_destroyed_notification_t not; - mach_msg_size_t size = sizeof(not) - sizeof(not.trailer); - - not.not_header.msgh_id = DEMAND_REQUEST; - not.not_header.msgh_remote_port = backup_port; - not.not_header.msgh_local_port = MACH_PORT_NULL; - not.not_header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0); - not.not_header.msgh_size = size; - not.not_body.msgh_descriptor_count = 1; - not.not_port.type = MACH_MSG_PORT_DESCRIPTOR; - not.not_port.disposition = MACH_MSG_TYPE_PORT_NAME; - not.not_port.name = about; - return mach_msg(¬.not_header, MACH_SEND_MSG|options, size, - 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); -} - -static void -notify_server_loop(mach_port_name_t about) -{ - mach_msg_return_t result; - - result = inform_server_loop(about, MACH_MSG_OPTION_NONE); - if (result != MACH_MSG_SUCCESS) - syslog(LOG_ERR, "notify_server_loop: mach_msg(): %s", mach_error_string(result)); -} - -void mach_start_shutdown(__unused int signalnum) -{ - shutdown_in_progress = TRUE; - (void) inform_server_loop(MACH_PORT_NULL, MACH_SEND_TIMEOUT); -} - -mach_port_t mach_init_init(void) -{ - kern_return_t result; - pthread_attr_t attr; - - bootstrap_self = mach_task_self(); - inherited_uid = getuid(); - getaudit(&inherited_audit); - init_lists(); - init_ports(); - - result = task_get_bootstrap_port(bootstrap_self, &inherited_bootstrap_port); - if (result != KERN_SUCCESS) { - syslog(LOG_ALERT, "task_get_bootstrap_port(): %s", mach_error_string(result)); - exit(EXIT_FAILURE); - } - if (inherited_bootstrap_port == MACH_PORT_NULL) - forward_ok = FALSE; - - /* We set this explicitly as we start each child */ - task_set_bootstrap_port(bootstrap_self, MACH_PORT_NULL); - - /* register "self" port with anscestor */ - if (register_self && forward_ok) { - result = bootstrap_register(inherited_bootstrap_port, - (char *)register_name, - bootstraps.bootstrap_port); - if (result != KERN_SUCCESS) - panic("register self(): %s", mach_error_string(result)); - } - - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - result = pthread_create(&demand_thread, &attr, demand_loop, NULL); - if (result) - panic("pthread_create(): %s", strerror(result)); - pthread_attr_destroy(&attr); - - return bootstraps.bootstrap_port; -} - -static void -init_ports(void) -{ - kern_return_t result; - - /* - * This task will become the bootstrap task. - */ - /* Create port set that server loop listens to */ - result = mach_port_allocate( - bootstrap_self, - MACH_PORT_RIGHT_PORT_SET, - &bootstrap_port_set); - if (result != KERN_SUCCESS) - panic("port_set_allocate(): %s", mach_error_string(result)); - - /* Create demand port set that second thread listens to */ - result = mach_port_allocate( - bootstrap_self, - MACH_PORT_RIGHT_PORT_SET, - &demand_port_set); - if (result != KERN_SUCCESS) - panic("port_set_allocate(): %s", mach_error_string(result)); - - /* Create notify port and add to server port set */ - result = mach_port_allocate( - bootstrap_self, - MACH_PORT_RIGHT_RECEIVE, - ¬ify_port); - if (result != KERN_SUCCESS) - panic("mach_port_allocate(): %s", mach_error_string(result)); - - result = mach_port_move_member( - bootstrap_self, - notify_port, - bootstrap_port_set); - if (result != KERN_SUCCESS) - panic("mach_port_move_member(): %s", mach_error_string(result)); - - /* Create backup port and add to server port set */ - result = mach_port_allocate( - bootstrap_self, - MACH_PORT_RIGHT_RECEIVE, - &backup_port); - if (result != KERN_SUCCESS) - panic("mach_port_allocate(): %s", mach_error_string(result)); - - result = mach_port_move_member( - bootstrap_self, - backup_port, - bootstrap_port_set); - if (result != KERN_SUCCESS) - panic("mach_port_move_member(): %s", mach_error_string(result)); - - /* Create "self" port and add to server port set */ - result = mach_port_allocate( - bootstrap_self, - MACH_PORT_RIGHT_RECEIVE, - &bootstraps.bootstrap_port); - if (result != KERN_SUCCESS) - panic("mach_port_allocate(): %s", mach_error_string(result)); - result = mach_port_insert_right( - bootstrap_self, - bootstraps.bootstrap_port, - bootstraps.bootstrap_port, - MACH_MSG_TYPE_MAKE_SEND); - if (result != KERN_SUCCESS) - panic("mach_port_insert_right(): %s", mach_error_string(result)); - - /* keep the root bootstrap port "active" */ - bootstraps.requestor_port = bootstraps.bootstrap_port; - - result = mach_port_move_member( - bootstrap_self, - bootstraps.bootstrap_port, - bootstrap_port_set); - if (result != KERN_SUCCESS) - panic("mach_port_move_member(): %s", mach_error_string(result)); -} - -boolean_t -active_bootstrap(bootstrap_info_t *bootstrap) -{ - return (bootstrap->requestor_port != MACH_PORT_NULL); -} - -boolean_t -useless_server(server_t *serverp) -{ - return ( !active_bootstrap(serverp->bootstrap) || - !lookup_service_by_server(serverp) || - !serverp->activity); -} - -boolean_t -active_server(server_t *serverp) -{ - return ( serverp->port || - serverp->task_port || serverp->active_services); -} - -static void -reap_server(server_t *serverp) -{ - kern_return_t result; - pid_t presult; - int wstatus; - - /* - * Reap our children. - */ - presult = waitpid(serverp->pid, &wstatus, WNOHANG); - switch (presult) { - case -1: - syslog(LOG_DEBUG, "waitpid: cmd = %s: %m", serverp->cmd); - break; - - case 0: - { - /* process must have switched mach tasks */ - mach_port_t old_port; - - old_port = serverp->task_port; - mach_port_deallocate(mach_task_self(), old_port); - serverp->task_port = MACH_PORT_NULL; - - result = task_for_pid( mach_task_self(), - serverp->pid, - &serverp->task_port); - if (result != KERN_SUCCESS) { - syslog(LOG_INFO, "race getting new server task port for pid[%d]: %s", - serverp->pid, mach_error_string(result)); - break; - } - - /* Request dead name notification to tell when new task dies */ - result = mach_port_request_notification( - mach_task_self(), - serverp->task_port, - MACH_NOTIFY_DEAD_NAME, - 0, - notify_port, - MACH_MSG_TYPE_MAKE_SEND_ONCE, - &old_port); - if (result != KERN_SUCCESS) { - syslog(LOG_INFO, "race setting up notification for new server task port for pid[%d]: %s", - serverp->pid, mach_error_string(result)); - break; - } - return; - } - - default: - if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus)) { - syslog(LOG_NOTICE, "Server %x in bootstrap %x uid %d: \"%s\"[%d]: exited with status: %d", - serverp->port, serverp->bootstrap->bootstrap_port, - serverp->uid, serverp->cmd, serverp->pid, WEXITSTATUS(wstatus)); - } else if (WIFSIGNALED(wstatus)) { - syslog(LOG_NOTICE, "Server %x in bootstrap %x uid %d: \"%s\"[%d]: exited abnormally: %s", - serverp->port, serverp->bootstrap->bootstrap_port, - serverp->uid, serverp->cmd, serverp->pid, strsignal(WTERMSIG(wstatus))); - } - break; - } - - - serverp->pid = 0; - - /* - * Release the server task port reference, if we ever - * got it in the first place. - */ - if (serverp->task_port != MACH_PORT_NULL) { - result = mach_port_deallocate( - mach_task_self(), - serverp->task_port); - if (result != KERN_SUCCESS) - syslog(LOG_ERR, "mach_port_deallocate(): %s", mach_error_string(result)); - serverp->task_port = MACH_PORT_NULL; - } -} - -static void -demand_server(server_t *serverp) -{ - service_t *servicep; - kern_return_t result; - - /* - * For on-demand servers, make sure that the service ports are - * back in on-demand portset. Active service ports should come - * back through a PORT_DESTROYED notification. We only have to - * worry about the inactive ports that may have been previously - * pulled from the set but never checked-in by the server. - */ - - for ( servicep = FIRST(services) - ; !IS_END(servicep, services) - ; servicep = NEXT(servicep)) - { - if (serverp == servicep->server && !servicep->isActive) { - result = mach_port_move_member( - mach_task_self(), - servicep->port, - demand_port_set); - if (result != KERN_SUCCESS) - panic("mach_port_move_member(): %s", mach_error_string(result)); - } - } -} - -static -void dispatch_server(server_t *serverp) -{ - if (!active_server(serverp)) { - if (useless_server(serverp) || shutdown_in_progress) - delete_server(serverp); - else if (serverp->servertype == RESTARTABLE) - start_server(serverp); - else if (serverp->servertype == DEMAND) - demand_server(serverp); - } -} - -void -setup_server(server_t *serverp) -{ - kern_return_t result; - mach_port_t old_port; - - /* Allocate privileged port for requests from service */ - result = mach_port_allocate(mach_task_self(), - MACH_PORT_RIGHT_RECEIVE , - &serverp->port); - syslog(LOG_INFO, "Allocating port %x for server %s", serverp->port, serverp->cmd); - if (result != KERN_SUCCESS) - panic("port_allocate(): %s", mach_error_string(result)); - - /* Request no-senders notification so we can tell when server dies */ - result = mach_port_request_notification(mach_task_self(), - serverp->port, - MACH_NOTIFY_NO_SENDERS, - 1, - serverp->port, - MACH_MSG_TYPE_MAKE_SEND_ONCE, - &old_port); - if (result != KERN_SUCCESS) - panic("mach_port_request_notification(): %s", mach_error_string(result)); - - /* Add privileged server port to bootstrap port set */ - result = mach_port_move_member(mach_task_self(), - serverp->port, - bootstrap_port_set); - if (result != KERN_SUCCESS) - panic("mach_port_move_member(): %s", mach_error_string(result)); -} - -pid_t -fork_with_bootstrap_port(mach_port_t p) -{ - static pthread_mutex_t forklock = PTHREAD_MUTEX_INITIALIZER; - kern_return_t result; - pid_t r; - size_t i; - - pthread_mutex_lock(&forklock); - - sigprocmask(SIG_BLOCK, &blocked_signals, NULL); - - result = task_set_bootstrap_port(mach_task_self(), p); - if (result != KERN_SUCCESS) - panic("task_set_bootstrap_port(): %s", mach_error_string(result)); - - if (launchd_bootstrap_port != p) { - result = mach_port_deallocate(mach_task_self(), p); - if (result != KERN_SUCCESS) - panic("mach_port_deallocate(): %s", mach_error_string(result)); - } - - r = fork(); - - if (r > 0) { - /* Post Tiger: - * - * We should set the bootstrap back to MACH_PORT_NULL instead - * of launchd_bootstrap_port. This will expose rare latent race - * condition bugs, given that some programs assume that the PID - * 1's bootstrap port is constant. This function clearly - * demonstrates that is no longer true. - * - * Those programs should be calling bootstrap_parent(), and not - * task_for_pid(1) followed by a call to get the bootstrap port - * on the task. - */ - result = task_set_bootstrap_port(mach_task_self(), launchd_bootstrap_port); - if (result != KERN_SUCCESS) - panic("task_set_bootstrap_port(): %s", mach_error_string(result)); - } else { - for (i = 0; i <= NSIG; i++) { - if (sigismember(&blocked_signals, i)) - signal(i, SIG_DFL); - } - } - - sigprocmask(SIG_UNBLOCK, &blocked_signals, NULL); - - pthread_mutex_unlock(&forklock); - - return r; -} - -static void -start_server(server_t *serverp) -{ - kern_return_t result; - mach_port_t old_port; - int pid; - - /* - * Do what's appropriate to get bootstrap port setup in server task - */ - switch (serverp->servertype) { - - case MACHINIT: - break; - - case SERVER: - case DEMAND: - case RESTARTABLE: - if (!serverp->port) - setup_server(serverp); - - serverp->activity = 0; - - /* Insert a send right */ - result = mach_port_insert_right(mach_task_self(), - serverp->port, - serverp->port, - MACH_MSG_TYPE_MAKE_SEND); - if (result != KERN_SUCCESS) - panic("mach_port_insert_right(): %s", mach_error_string(result)); - - pid = fork_with_bootstrap_port(serverp->port); - if (pid < 0) { - syslog(LOG_WARNING, "fork(): %m"); - } else if (pid == 0) { /* CHILD */ - exec_server(serverp); - _exit(EXIT_FAILURE); - } else { /* PARENT */ - syslog(LOG_INFO, "Launched server %x in bootstrap %x uid %d: \"%s\": [pid %d]", - serverp->port, serverp->bootstrap->bootstrap_port, - serverp->uid, serverp->cmd, pid); - serverp->pid = pid; - result = task_for_pid( - mach_task_self(), - pid, - &serverp->task_port); - if (result != KERN_SUCCESS) { - syslog(LOG_ERR, "getting server task port(): %s", mach_error_string(result)); - reap_server(serverp); - dispatch_server(serverp); - break; - } - - /* Request dead name notification to tell when task dies */ - result = mach_port_request_notification( - mach_task_self(), - serverp->task_port, - MACH_NOTIFY_DEAD_NAME, - 0, - notify_port, - MACH_MSG_TYPE_MAKE_SEND_ONCE, - &old_port); - if (result != KERN_SUCCESS) { - syslog(LOG_ERR, "mach_port_request_notification(): %s", mach_error_string(result)); - reap_server(serverp); - dispatch_server(serverp); - } - } - break; - } -} - -static void -exec_server(server_t *serverp) -{ - char **argv; - sigset_t mask; - - /* - * Setup environment for server, someday this should be Mach stuff - * rather than Unix crud - */ - argv = argvize(serverp->cmd); - closelog(); - - /* - * Set up the audit state for the user (if necessesary). - */ - if (inherited_uid == 0 && - (serverp->auinfo.ai_auid != inherited_uid || - serverp->auinfo.ai_asid != inherited_audit.ai_asid)) { - struct passwd *pwd = NULL; - - pwd = getpwuid(serverp->auinfo.ai_auid); - if (pwd == NULL) { - panic("Disabled server %x bootstrap %x: \"%s\": getpwuid(%d) failed", - serverp->port, serverp->bootstrap->bootstrap_port, - serverp->cmd, serverp->auinfo.ai_auid); - - } else if (au_user_mask(pwd->pw_name, &serverp->auinfo.ai_mask) != 0) { - panic("Disabled server %x bootstrap %x: \"%s\": au_user_mask(%s) failed", - serverp->port, serverp->bootstrap->bootstrap_port, - serverp->cmd, pwd->pw_name); - } else if (setaudit(&serverp->auinfo) != 0) - panic("Disabled server %x bootstrap %x: \"%s\": setaudit()", - serverp->port, serverp->bootstrap->bootstrap_port, - serverp->cmd); - } - - if (serverp->uid != inherited_uid) { - struct passwd *pwd = getpwuid(serverp->uid); - gid_t g; - - if (NULL == pwd) { - panic("Disabled server %x bootstrap %x: \"%s\": getpwuid(%d) failed", - serverp->port, serverp->bootstrap->bootstrap_port, - serverp->cmd, serverp->uid); - } - - g = pwd->pw_gid; - - if (-1 == setgroups(1, &g)) { - panic("Disabled server %x bootstrap %x: \"%s\": setgroups(1, %d): %s", - serverp->port, serverp->bootstrap->bootstrap_port, - serverp->cmd, g, strerror(errno)); - } - - if (-1 == setgid(g)) { - panic("Disabled server %x bootstrap %x: \"%s\": setgid(%d): %s", - serverp->port, serverp->bootstrap->bootstrap_port, - serverp->cmd, g, strerror(errno)); - } - - if (-1 == setuid(serverp->uid)) { - panic("Disabled server %x bootstrap %x: \"%s\": setuid(%d): %s", - serverp->port, serverp->bootstrap->bootstrap_port, - serverp->cmd, serverp->uid, strerror(errno)); - } - } - - - if (setsid() < 0) { - /* - * We can't keep this from happening, but we shouldn't start - * the server not as a process group leader. So, just fake like - * there was real activity, and exit the child. If needed, - * we'll re-launch it under another pid. - */ - serverp->activity = 1; - panic("Temporary failure server %x bootstrap %x: \"%s\": setsid(): %s", - serverp->port, serverp->bootstrap->bootstrap_port, - serverp->cmd, strerror(errno)); - } - - sigemptyset(&mask); - (void) sigprocmask(SIG_SETMASK, &mask, (sigset_t *)NULL); - - setpriority(PRIO_PROCESS, 0, 0); - execv(argv[0], argv); - panic("Disabled server %x bootstrap %x: \"%s\": exec(): %s", - serverp->port, - serverp->bootstrap->bootstrap_port, - serverp->cmd, - strerror(errno)); -} - -static char ** -argvize(const char *string) -{ - static char *argv[100], args[1000]; - const char *cp; - char *argp, term; - unsigned int nargs; - - /* - * Convert a command line into an argv for execv - */ - nargs = 0; - argp = args; - - for (cp = string; *cp;) { - while (isspace(*cp)) - cp++; - term = (*cp == '"') ? *cp++ : '\0'; - if (nargs < NELEM(argv)) - argv[nargs++] = argp; - while (*cp && (term ? *cp != term : !isspace(*cp)) - && argp < END_OF(args)) { - if (*cp == '\\') - cp++; - *argp++ = *cp; - if (*cp) - cp++; - } - *argp++ = '\0'; - } - argv[nargs] = NULL; - return argv; -} - -static void * -demand_loop(void *arg __attribute__((unused))) -{ - mach_msg_empty_rcv_t dummy; - kern_return_t dresult; - - - for(;;) { - mach_port_name_array_t members; - mach_msg_type_number_t membersCnt; - mach_port_status_t status; - mach_msg_type_number_t statusCnt; - unsigned int i; - - /* - * Receive indication of message on demand service - * ports without actually receiving the message (we'll - * let the actual server do that. - */ - dresult = mach_msg( - &dummy.header, - MACH_RCV_MSG|MACH_RCV_LARGE, - 0, - 0, - demand_port_set, - 0, - MACH_PORT_NULL); - if (dresult != MACH_RCV_TOO_LARGE) { - syslog(LOG_ERR, "demand_loop: mach_msg(): %s", mach_error_string(dresult)); - continue; - } - - /* - * Some port(s) now have messages on them, find out - * which ones (there is no indication of which port - * triggered in the MACH_RCV_TOO_LARGE indication). - */ - dresult = mach_port_get_set_status( - mach_task_self(), - demand_port_set, - &members, - &membersCnt); - if (dresult != KERN_SUCCESS) { - syslog(LOG_ERR, "demand_loop: mach_port_get_set_status(): %s", mach_error_string(dresult)); - continue; - } - - for (i = 0; i < membersCnt; i++) { - statusCnt = MACH_PORT_RECEIVE_STATUS_COUNT; - dresult = mach_port_get_attributes( - mach_task_self(), - members[i], - MACH_PORT_RECEIVE_STATUS, - (mach_port_info_t)&status, - &statusCnt); - if (dresult != KERN_SUCCESS) { - syslog(LOG_ERR, "demand_loop: mach_port_get_attributes(): %s", mach_error_string(dresult)); - continue; - } - - /* - * For each port with messages, take it out of the - * demand service portset, and inform the main thread - * that it might have to start the server responsible - * for it. - */ - if (status.mps_msgcount) { - dresult = mach_port_move_member( - mach_task_self(), - members[i], - MACH_PORT_NULL); - if (dresult != KERN_SUCCESS) { - syslog(LOG_ERR, "demand_loop: mach_port_move_member(): %s", mach_error_string(dresult)); - continue; - } - notify_server_loop(members[i]); - } - } - - dresult = vm_deallocate( - mach_task_self(), - (vm_address_t) members, - (vm_size_t) membersCnt * sizeof(mach_port_name_t)); - if (dresult != KERN_SUCCESS) { - syslog(LOG_ERR, "demand_loop: vm_deallocate(): %s", mach_error_string(dresult)); - continue; - } - } - return NULL; -} - -/* - * server_demux -- processes requests off our service port - * Also handles notifications - */ - -static boolean_t -server_demux( - mach_msg_header_t *Request, - mach_msg_header_t *Reply) -{ - bootstrap_info_t *bootstrap; - service_t *servicep; - server_t *serverp; - kern_return_t result; - mig_reply_error_t *reply; - - syslog(LOG_DEBUG, "received message on port %x", Request->msgh_local_port); - - reply = (mig_reply_error_t *)Reply; - - /* - * Pick off notification messages - */ - if (Request->msgh_local_port == notify_port) { - mach_port_name_t np; - - memset(reply, 0, sizeof(*reply)); - switch (Request->msgh_id) { - case MACH_NOTIFY_DEAD_NAME: - np = ((mach_dead_name_notification_t *)Request)->not_port; - syslog(LOG_DEBUG, "Notified dead name %x", np); - - if (np == inherited_bootstrap_port) { - inherited_bootstrap_port = MACH_PORT_NULL; - forward_ok = FALSE; - } - - /* - * Check to see if a subset requestor port was deleted. - */ - while ((bootstrap = lookup_bootstrap_by_req_port(np)) != NULL) { - syslog(LOG_DEBUG, "Received dead name notification for bootstrap subset %x requestor port %x", - bootstrap->bootstrap_port, bootstrap->requestor_port); - mach_port_deallocate( - mach_task_self(), - bootstrap->requestor_port); - bootstrap->requestor_port = MACH_PORT_NULL; - deactivate_bootstrap(bootstrap); - } - - /* - * Check to see if a defined service has gone - * away. - */ - while ((servicep = lookup_service_by_port(np)) != NULL) { - /* - * Port gone, registered service died. - */ - syslog(LOG_DEBUG, "Received dead name notification for service %s " - "on bootstrap port %x\n", - servicep->name, servicep->bootstrap); - syslog(LOG_DEBUG, "Service %s failed - deallocate", servicep->name); - delete_service(servicep); - } - - /* - * Check to see if a launched server task has gone - * away. - */ - if ((serverp = lookup_server_by_task_port(np)) != NULL) { - /* - * Port gone, server died or picked up new task. - */ - syslog(LOG_DEBUG, "Received task death notification for server %s ", - serverp->cmd); - reap_server(serverp); - dispatch_server(serverp); - } - - mach_port_deallocate(mach_task_self(), np); - reply->RetCode = KERN_SUCCESS; - break; - - case MACH_NOTIFY_PORT_DELETED: - np = ((mach_port_deleted_notification_t *)Request)->not_port; - syslog(LOG_DEBUG, "port deleted notification on 0x%x", np); - reply->RetCode = KERN_SUCCESS; - break; - - case MACH_NOTIFY_SEND_ONCE: - syslog(LOG_DEBUG, "notification send-once right went unused"); - reply->RetCode = KERN_SUCCESS; - break; - - default: - syslog(LOG_ERR, "Unexpected notification: %d", Request->msgh_id); - reply->RetCode = KERN_FAILURE; - break; - } - } - - else if (Request->msgh_local_port == backup_port) { - mach_port_name_t np; - - memset(reply, 0, sizeof(*reply)); - - np = ((mach_port_destroyed_notification_t *)Request)->not_port.name; - servicep = lookup_service_by_port(np); - if (servicep != NULL) { - serverp = servicep->server; - - switch (Request->msgh_id) { - - case MACH_NOTIFY_PORT_DESTROYED: - /* - * Port sent back to us, server died. - */ - syslog(LOG_DEBUG, "Received destroyed notification for service %s", - servicep->name); - syslog(LOG_DEBUG, "Service %x bootstrap %x backed up: %s", - servicep->port, servicep->bootstrap->bootstrap_port, - servicep->name); - ASSERT(canReceive(servicep->port)); - servicep->isActive = FALSE; - serverp->active_services--; - dispatch_server(serverp); - reply->RetCode = KERN_SUCCESS; - break; - - case DEMAND_REQUEST: - /* message reflected over from demand start thread */ - if (!active_server(serverp)) - start_server(serverp); - reply->RetCode = KERN_SUCCESS; - break; - - default: - syslog(LOG_DEBUG, "Mysterious backup_port notification %d", Request->msgh_id); - reply->RetCode = KERN_FAILURE; - break; - } - } else { - syslog(LOG_DEBUG, "Backup_port notification - previously deleted service"); - reply->RetCode = KERN_FAILURE; - } - } - - else if (Request->msgh_id == MACH_NOTIFY_NO_SENDERS) { - mach_port_t ns = Request->msgh_local_port; - - if ((serverp = lookup_server_by_port(ns)) != NULL_SERVER) { - /* - * A server we launched has released his bootstrap - * port send right. We won't re-launch him unless - * his services came back to roost. But we need to - * destroy the bootstrap port for fear of leaking. - */ - syslog(LOG_DEBUG, "server %s dropped server port", serverp->cmd); - serverp->port = MACH_PORT_NULL; - dispatch_server(serverp); - } else if ((bootstrap = lookup_bootstrap_by_port(ns)) != NULL) { - /* - * The last direct user of a deactivated bootstrap went away. - * We can finally free it. - */ - syslog(LOG_DEBUG, "Deallocating bootstrap %x: no more clients", ns); - bootstrap->bootstrap_port = MACH_PORT_NULL; - deallocate_bootstrap(bootstrap); - } - - result = mach_port_mod_refs( - mach_task_self(), - ns, - MACH_PORT_RIGHT_RECEIVE, - -1); - if (result != KERN_SUCCESS) - panic("mach_port_mod_refs(): %s", mach_error_string(result)); - - memset(reply, 0, sizeof(*reply)); - reply->RetCode = KERN_SUCCESS; - } - - else { /* must be a service request */ - syslog(LOG_DEBUG, "Handled request."); - return bootstrap_server(Request, Reply); - } - return TRUE; -} - -/* - * server_loop -- pick requests off our service port and process them - * Also handles notifications - */ -#define bootstrapMaxRequestSize 1024 -#define bootstrapMaxReplySize 1024 - -static boolean_t -server_demux_with_giant_lock( - mach_msg_header_t *Request, - mach_msg_header_t *Reply) -{ - boolean_t r; - - assumes(pthread_mutex_lock(&giant_lock) == 0); - r = server_demux(Request, Reply); - assumes(pthread_mutex_unlock(&giant_lock) == 0); - - return r; -} - -void * -mach_server_loop(void *arg __attribute__((unused))) -{ - mach_msg_return_t mresult; - - for (;;) { - mresult = mach_msg_server( - server_demux_with_giant_lock, - bootstrapMaxRequestSize, - bootstrap_port_set, - MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_SENDER)| - MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0)); - if (mresult != MACH_MSG_SUCCESS) - syslog(LOG_ERR, "mach_msg_server(): %s", mach_error_string(mresult)); - } - return NULL; -} - -bool -canReceive(mach_port_t port) -{ - mach_port_type_t p_type; - kern_return_t result; - - result = mach_port_type(mach_task_self(), port, &p_type); - if (result != KERN_SUCCESS) { - syslog(LOG_ERR, "port_type(): %s", mach_error_string(result)); - return FALSE; - } - return ((p_type & MACH_PORT_TYPE_RECEIVE) != 0); -} - - -bool -canSend(mach_port_t port) -{ - mach_port_type_t p_type; - kern_return_t result; - - result = mach_port_type(mach_task_self(), port, &p_type); - if (result != KERN_SUCCESS) { - syslog(LOG_ERR, "port_type(): %s", mach_error_string(result)); - return FALSE; - } - return ((p_type & MACH_PORT_TYPE_PORT_RIGHTS) != 0); -} - -/* Sigh... Libc's panic() call fails to abort and continues afterwards */ -void -panic(const char *msg, ...) -{ - va_list ap; - - va_start(ap, msg); - vfprintf(stderr, msg, ap); - va_end(ap); - - _exit(EXIT_FAILURE); -} diff --git a/launchd/src/bootstrap.defs b/launchd/src/bootstrap.defs index cc7f9e8..9d0220f 100644 --- a/launchd/src/bootstrap.defs +++ b/launchd/src/bootstrap.defs @@ -1,360 +1,128 @@ /* * Copyright (c) 1999-2004 Apple Computer, Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ + * @APPLE_APACHE_LICENSE_HEADER_START@ * - * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights - * Reserved. This file contains Original Code and/or Modifications of - * Original Code as defined in and that are subject to the Apple Public - * Source License Version 1.0 (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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * The 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." + * http://www.apache.org/licenses/LICENSE-2.0 * - * @APPLE_LICENSE_HEADER_END@ + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @APPLE_APACHE_LICENSE_HEADER_END@ */ /* * bootstrap -- fundamental service initiator and port server * Mike DeMoney, NeXT, Inc. * Copyright, 1990. All rights reserved. - * - * bootstrap.defs -- Mig interface definition */ subsystem bootstrap 400; -/* - * Interface: Bootstrap server - * - * The bootstrap server is the first user-mode task initiated by the Mach - * kernel at system boot time. The bootstrap server provides two services, - * it initiates other system tasks, and manages a table of name-port bindings - * for fundamental system services (e.g. lookupd, Window Manager, etc...). - * - * Name-port bindings can be established with the bootstrap server by either - * of two mechanisms: - * - * 1. The binding can be indicated, in advance of the service that backs it - * being available, via a "service create" request. In this case, bootstrap - * will immediately create a port and bind the indicated name with that port. - * At a later time, a service may "checkin" for the name-port - * binding and will be returned receive rights for the bound port. Lookup's - * on bindings created by this mechanism will return send rights to the port, - * even if no service has "checked-in". In this case, requests sent to the - * bound port will be queued until a server has checked-in and can satisfy the - * request. - * - * 2. Bindings can be established dynamically via a "register" request. In - * this case, the register request provides bootstrap with a name and send - * rights for a port. Bootstrap will provide send rights for the bound port - * to any requestor via the lookup request. - * - * Bootstrap provides its service port to descendant tasks via the Mach - * "bootstrap" special task port. All direct descendants of bootstrap receive - * a "privileged" bootstrap service port. System services that initiate - * untrusted tasks should replace the Mach bootstrap task special port with - * a subset bootstrap port to prevent them from infecting the namespace. - * - * The bootstrap server creates a "backup" port for each service that it - * creates. This is used to detect when a checked out service is no longer - * being served. The bootstrap server regains all rights to the port and - * it is marked available for check-out again. This allows crashed servers to - * resume service to previous clients. Lookup's on this named port will - * continue to be serviced by bootstrap while holding receive rights for the - * bound port. A client may detect that the service is inactive via the - * bootstrap status request. If an inactive service re-registers rather - * than "checking-in" the original bound port is destroyed. - * - * The status of a named service may be obtained via the "status" request. - * A service is "active" if a name-port binding exists and receive rights - * to the bound port are held by a task other than bootstrap. - * - * The bootstrap server may also (re)start server processes associated with - * with a set of services. The definition of the server process is done - * through the "create server" request. The server will be launched in the - * same bootstrap context in which it was registered. - */ - #include #include -import ; +import "bootstrap_public.h"; +import "bootstrap_private.h"; +type pid_t = integer_t; type cmd_t = c_string[512]; -type name_t = c_string[128]; type cmd_array_t = ^array [] of cmd_t; +type name_t = c_string[128]; type name_array_t = ^array [] of name_t; +type _internal_string_t = ^array [] of char; type bootstrap_status_t = integer_t; type bootstrap_status_array_t = ^array [] of bootstrap_status_t; +userprefix raw_; serverprefix x_; -/* - * kern_return_t - * bootstrap_create_server(mach_port_t __bs_port, - * cmd_t __server_command, - * natural_t __server_uid, - * boolean_t __on_demand, - * mach_port_t *__server_port) - * - * Declares a server that mach_init will re-spawn within the specified - * bootstrap context. The server is considered already "active" - * (i.e. will not be re-spawned) until the returned server_port is - * deallocated. - * - * In the meantime, services can be declared against the server, - * by using the server_port as the privileged bootstrap target of - * subsequent bootstrap_create_service() calls. - * - * When mach_init re-spawns the server, its task bootstrap port - * is set to the privileged sever_port. Through this special - * bootstrap port, it can access all of parent bootstrap's context - * (and all services are created in the parent's namespace). But - * all additional service declarations (and declaration removals) - * will be associated with this particular server. - * - * Only a holder of the server_port privilege bootstrap port can - * check in or register over those services. - * - * When all services associated with a server are deleted, and the server - * exits, it will automatically be deleted itself. - * - * If the server is declared "on_demand," then a non-running server - * will be re-launched on first use of one of the service ports - * registered against it. Otherwise, it will be re-launched - * immediately upon exiting (whether any client is actively using - * any of the service ports or not). - * - * Errors: Returns appropriate kernel errors on rpc failure. - * Returns BOOTSTRAP_NOT_PRIVILEGED, bootstrap or uid invalid. - */ routine bootstrap_create_server( __bs_port : mach_port_t; __server_cmd : cmd_t; __server_uid : natural_t; __on_demand : boolean_t; - ServerAuditToken token : audit_token_t; + ServerAuditToken __token : audit_token_t; out __server_port : mach_port_make_send_t); -/* - * kern_return_t - * bootstrap_unprivileged(mach_port_t __bs_port, - * mach_port_t *__unpriv_port) - * - * Given a bootstrap port, return its unprivileged equivalent. If - * the port is already unprivileged, another reference to the same - * port is returned. - * - * This is most often used by servers, which are launched with their - * bootstrap port set to the privileged port for the server, to get - * an unprivileged version of the same port for use by its unprivileged - * children (or any offspring that it does not want to count as part - * of the "server" for mach_init registration and re-launch purposes). - */ routine bootstrap_unprivileged( __bs_port : mach_port_t; - out __unpriv_port : mach_port_t); + out __unpriv_port : mach_port_make_send_t); -/* - * kern_return_t - * bootstrap_check_in(mach_port_t __bs_port, - * name_t __service_name, - * mach_port_t *__service_port) - * - * Returns the receive right for the service named by service_name. The - * service must have previously been declared in this bootstrap context via - * a call to bootstrap_create_service(). Attempts to check_in a service - * which is already active are not allowed. - * - * If the service was declared as being associated with a server, the - * check_in must come from the server's privileged port (server_port). - * - * Errors: Returns appropriate kernel errors on rpc failure. - * Returns BOOTSTRAP_UNKNOWN_SERVICE, if service does not exist. - * Returns BOOTSTRAP_NOT_PRIVILEGED, if request directed to - * bootstrap port without privilege. - * Returns BOOTSTRAP_SERVICE_ACTIVE, if service has already been - * registered or checked-in. - */ routine bootstrap_check_in( __bs_port : mach_port_t; __service_name : name_t; + ServerAuditToken __token : audit_token_t; out __service_port : mach_port_move_receive_t); -/* - * kern_return_t - * bootstrap_register(mach_port_t __bs_port, - * name_t __service_name, - * mach_port_t __service_port) - * - * Registers a send right for service_port with the service identified by - * service_name. Attempts to register a service where an active binding - * already exists are rejected. - * - * If the service was previously declared with bootstrap_create_service(), - * but is not currently active, this call can be used to undeclare the - * service. The bootstrap port used must have sufficient privilege to - * do so. (Registering MACH_PORT_NULL is especially useful for shutting - * down declared services). - * - * Errors: Returns appropriate kernel errors on rpc failure. - * Returns BOOTSTRAP_NOT_PRIVILEGED, if request directed to - * bootstrap port without privilege. - * Returns BOOTSTRAP_NAME_IN_USE, if service has already been - * register or checked-in. - */ routine bootstrap_register( __bs_port : mach_port_t; + ServerAuditToken __token : audit_token_t; __service_name : name_t; __service_port : mach_port_t); -/* - * kern_return_t - * bootstrap_look_up(mach_port_t __bs_port, - * name_t __service_name, - * mach_port_t *__service_port) - * - * Returns a send right for the service port declared/registered under the - * name service_name. The service is not guaranteed to be active. Use the - * bootstrap_status call to determine the status of the service. - * - * Errors: Returns appropriate kernel errors on rpc failure. - * Returns BOOTSTRAP_UNKNOWN_SERVICE, if service does not exist. - */ routine bootstrap_look_up( __bs_port : mach_port_t; + ServerAuditToken __token : audit_token_t; __service_name : name_t; - out __service_port : mach_port_t); + out __service_port : mach_port_send_t); -/* - * kern_return_t - * bootstrap_look_up_array(mach_port_t __bs_port, - * name_array_t __service_names, - * int __service_names_cnt, - * port_array_t *__service_port, - * int *__service_ports_cnt, - * boolean_t *__all_services_known) - * - * Returns port send rights in corresponding entries of the array service_ports - * for all services named in the array service_names. Service_ports_cnt is - * returned and will always equal service_names_cnt (assuming service_names_cnt - * is greater than or equal to zero). - * - * Errors: Returns appropriate kernel errors on rpc failure. - * Returns BOOTSTRAP_NO_MEMORY, if server couldn't obtain memory - * for response. - * Unknown service names have the corresponding service port set - * to PORT_NULL. - * If all services are known, all_services_known is true on - * return, if any service is unknown, it's false. - */ -routine bootstrap_look_up_array( - __bs_port : mach_port_t; - __service_names : name_array_t; - out __service_ports : mach_port_array_t; - out __all_services_known: boolean_t); +skip; /* last used in 10.4 */ -/* - * kern_return_t - * bootstrap_parent(mach_port_t __bs_port, - * mach_port_t *__parent_port); - * - * Given a bootstrap subset port, return the parent bootstrap port. - * If the specified bootstrap port is already the root subset, - * MACH_PORT_NULL will be returned. - * - * Errors: - * Returns BOOTSTRAP_NOT_PRIVILEGED if the caller is not running - * with an effective user id of root (as determined by the security - * token in the message trailer). - */ routine bootstrap_parent( __bs_port : mach_port_t; - ServerSecToken __token : security_token_t; - out __parent_port : mach_port_make_send_t); + out __parent_port : mach_port_send_t); -/* - * kern_return_t - * bootstrap_status(mach_port_t __bs_port, - * name_t __service_name, - * bootstrap_status_t *__service_active); - * - * Returns: service_active indicates if service is active, inactive, or - * associated with a launch-on-demand server. - * - * Errors: Returns appropriate kernel errors on rpc failure. - * Returns BOOTSTRAP_UNKNOWN_SERVICE, if service does not exist. - */ -routine bootstrap_status( - __bs_port : mach_port_t; - __service_name : name_t; - out __service_active: bootstrap_status_t); +skip; /* last used in 10.4 */ -/* - * kern_return_t - * bootstrap_info(port_t __bs_port, - * name_array_t *__service_names, - * int *__service_names_cnt, - * name_array_t *__server_names, - * int *__server_names_cnt, - * bool_array_t *__service_active, - * int *__service_active_cnt); - * - * Errors: Returns appropriate kernel errors on rpc failure. - */ routine bootstrap_info( __bs_port : mach_port_t; out __service_names : name_array_t, dealloc; - out __server_names : name_array_t, dealloc; out __service_active : bootstrap_status_array_t, dealloc); -/* - * kern_return_t - * bootstrap_subset(mach_port_t __bs_port, - * mach_port_t __requestor_port, - * mach_port_t *__subset_port); - * - * Returns a new port to use as a bootstrap port. This port behaves - * exactly like the previous bootstrap_port, except that ports dynamically - * registered via bootstrap_register() are available only to users of this - * specific subset_port. Lookups on the subset_port will return ports - * registered with this port specifically, and ports registered with - * ancestors of this subset_port. Duplications of services already - * registered with an ancestor port may be registered with the subset port - * are allowed. Services already advertised may then be effectively removed - * by registering PORT_NULL for the service. - * When it is detected that the requestor_port is destroyed the subset - * port and all services advertized by it are destroied as well. - * - * Errors: Returns appropriate kernel errors on rpc failure. - */ routine bootstrap_subset( __bs_port : mach_port_t; __requestor_port: mach_port_t; - out __subset_port : mach_port_t); + out __subset_port : mach_port_make_send_t); -/* - * kern_return_t - * bootstrap_create_service(mach_port_t __bs_port, - * name_t __service_name, - * mach_port_t *__service_port) - * - * Creates a service named "service_name" and returns send rights to that - * port in "service_port." The port may later be checked in as if this - * port were configured in the bootstrap configuration file. - * - * Errors: Returns appropriate kernel errors on rpc failure. - * Returns BOOTSTRAP_SERVICE_ACTIVE, if service already exists. - */ routine bootstrap_create_service( __bs_port : mach_port_t; __service_name : name_t; out __service_port : mach_port_t); +routine bootstrap_transfer_subset( + __bs_port : mach_port_t; + out __bs_reqport : mach_port_t; + out __bs_rcvright : mach_port_move_receive_t; + out __service_names : name_array_t, dealloc; + out __service_ports : mach_port_array_t, dealloc); + +routine bootstrap_getsocket( + __bs_port : mach_port_t; + out __sockpath : name_t); + +routine mpm_spawn( + __bs_port : mach_port_t; + ServerAuditToken __token : audit_token_t; + __chars : _internal_string_t; + __argc : uint32_t; + __envc : uint32_t; + __flags : uint64_t; + __umask : uint16_t; + out __pid : pid_t; + out __obsvr_port : mach_port_make_send_t); + +routine mpm_wait( + __bs_port : mach_port_t; + sreplyport __rport : mach_port_make_send_once_t; + ServerAuditToken __token : audit_token_t; + out __waitval : integer_t); + +routine mpm_uncork_fork( + __bs_port : mach_port_t; + ServerAuditToken __token : audit_token_t); diff --git a/launchd/src/bootstrap_internal.h b/launchd/src/bootstrap_internal.h deleted file mode 100644 index b2f4808..0000000 --- a/launchd/src/bootstrap_internal.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 1999-2003 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights - * Reserved. This file contains Original Code and/or Modifications of - * Original Code as defined in and that are subject to the Apple Public - * Source License Version 1.0 (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. - * - * The 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@ - */ -/* - * bootstrap -- fundamental service initiator and port server - * Mike DeMoney, NeXT, Inc. - * Copyright, 1990. All rights reserved. - * - * bootstrap_internal.h -- global internal data definitions - */ - -#include -#include -#include -#include - -#define BASEPRI_USER 31 /* AOF 20/02/2002 */ - -#define ANYWHERE TRUE /* For use with vm_allocate() */ - -#define DEMAND_REQUEST MACH_NOTIFY_LAST /* demand service messaged */ - -__private_extern__ void mach_start_shutdown(int); -__private_extern__ mach_port_t mach_init_init(void); -__private_extern__ void *mach_server_loop(void*); -__private_extern__ pid_t fork_with_bootstrap_port(mach_port_t); - -__private_extern__ mach_port_t lookup_only_port; -__private_extern__ mach_port_t inherited_bootstrap_port; -__private_extern__ mach_port_t self_port; /* Compatability hack */ -__private_extern__ bool forward_ok; -__private_extern__ bool debugging; -__private_extern__ mach_port_t bootstrap_port_set; -__private_extern__ mach_port_t demand_port_set; -__private_extern__ mach_port_t notify_port; -__private_extern__ mach_port_t backup_port; -__private_extern__ bool canReceive(mach_port_t); -__private_extern__ bool canSend(mach_port_t); -__private_extern__ bool register_self; -__private_extern__ const char *register_name; diff --git a/launchd/src/bootstrap_private.h b/launchd/src/bootstrap_private.h new file mode 100644 index 0000000..a2dace1 --- /dev/null +++ b/launchd/src/bootstrap_private.h @@ -0,0 +1,55 @@ +#ifndef _BOOTSTRAP_PRIVATE_H_ +#define _BOOTSTRAP_PRIVATE_H_ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_APACHE_LICENSE_HEADER_START@ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @APPLE_APACHE_LICENSE_HEADER_END@ + */ + +typedef char *_internal_string_t; +#define SPAWN_HAS_PATH 0x0001 +#define SPAWN_HAS_WDIR 0x0002 +#define SPAWN_HAS_UMASK 0x0004 +#define SPAWN_WANTS_WAIT4DEBUGGER 0x0008 +#define SPAWN_WANTS_FORCE_PPC 0x0010 + +kern_return_t +_launchd_to_launchd(mach_port_t bp, mach_port_t *reqport, mach_port_t *rcvright, + name_array_t *service_names, mach_msg_type_number_t *service_namesCnt, + mach_port_array_t *ports, mach_msg_type_number_t *portCnt); + +kern_return_t bootstrap_getsocket(mach_port_t bp, name_t); + + +kern_return_t +bootstrap_look_up_array( + mach_port_t bp, + name_array_t service_names, + mach_msg_type_number_t service_namesCnt, + mach_port_array_t *sps, + mach_msg_type_number_t *service_portsCnt, + boolean_t *all_services_known); + +kern_return_t +bootstrap_info( + mach_port_t bp, + name_array_t *service_names, + mach_msg_type_number_t *service_namesCnt, + bootstrap_status_array_t *service_active, + mach_msg_type_number_t *service_activeCnt); + +#endif diff --git a/launchd/src/bootstrap_public.c b/launchd/src/bootstrap_public.c new file mode 100644 index 0000000..1845ed6 --- /dev/null +++ b/launchd/src/bootstrap_public.c @@ -0,0 +1,285 @@ +/* + * Copyright (c) 1999-2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_APACHE_LICENSE_HEADER_START@ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @APPLE_APACHE_LICENSE_HEADER_END@ + */ + +#include +#include + +#include "bootstrap_public.h" +#include "bootstrap_private.h" +#include "launch.h" +#include "launch_priv.h" + +#define mig_external static +#include "bootstrap.h" +#include "bootstrapUser.c" + +#include +#include +#include + +kern_return_t +_launchd_to_launchd(mach_port_t bp, mach_port_t *reqport, mach_port_t *rcvright, + name_array_t *service_names, mach_msg_type_number_t *service_namesCnt, + mach_port_array_t *ports, mach_msg_type_number_t *portCnt) +{ + return raw_bootstrap_transfer_subset(bp, reqport, rcvright, service_names, service_namesCnt, ports, portCnt); +} + +pid_t +_spawn_via_launchd(const char *label, const char *const *argv, const struct spawn_via_launchd_attr *spawn_attrs, int struct_version) +{ + kern_return_t kr; + const char *const *tmpp; + size_t len, buf_len = strlen(label) + 1; + char *buf = strdup(label); + uint64_t flags = 0; + uint32_t argc = 0; + uint32_t envc = 0; + pid_t p = -1; + mode_t u_mask = CMASK; + mach_port_t obsvr_port = MACH_PORT_NULL; + + for (tmpp = argv; *tmpp; tmpp++) { + argc++; + len = strlen(*tmpp) + 1; + buf = reallocf(buf, buf_len + len); + strcpy(buf + buf_len, *tmpp); + buf_len += len; + } + + if (spawn_attrs) switch (struct_version) { + case 0: + if (spawn_attrs->spawn_flags & SPAWN_VIA_LAUNCHD_STOPPED) { + flags |= SPAWN_WANTS_WAIT4DEBUGGER; + } + if (spawn_attrs->spawn_flags & SPAWN_VIA_LAUNCHD_FORCE_PPC) { + flags |= SPAWN_WANTS_FORCE_PPC; + } + + if (spawn_attrs->spawn_env) { + for (tmpp = spawn_attrs->spawn_env; *tmpp; tmpp++) { + envc++; + len = strlen(*tmpp) + 1; + buf = reallocf(buf, buf_len + len); + strcpy(buf + buf_len, *tmpp); + buf_len += len; + } + } + + if (spawn_attrs->spawn_path) { + flags |= SPAWN_HAS_PATH; + len = strlen(spawn_attrs->spawn_path) + 1; + buf = reallocf(buf, buf_len + len); + strcpy(buf + buf_len, spawn_attrs->spawn_path); + buf_len += len; + } + + if (spawn_attrs->spawn_chdir) { + flags |= SPAWN_HAS_WDIR; + len = strlen(spawn_attrs->spawn_chdir) + 1; + buf = reallocf(buf, buf_len + len); + strcpy(buf + buf_len, spawn_attrs->spawn_chdir); + buf_len += len; + } + + if (spawn_attrs->spawn_umask) { + flags |= SPAWN_HAS_UMASK; + u_mask = *spawn_attrs->spawn_umask; + } + + break; + default: + break; + } + + kr = raw_mpm_spawn(bootstrap_port, buf, buf_len, argc, envc, flags, u_mask, &p, &obsvr_port); + + free(buf); + + if (kr == BOOTSTRAP_SUCCESS) { + if (spawn_attrs && spawn_attrs->spawn_observer_port) { + *spawn_attrs->spawn_observer_port = obsvr_port; + } else { + mach_port_deallocate(mach_task_self(), obsvr_port); + } + return p; + } + + switch (kr) { + case BOOTSTRAP_NOT_PRIVILEGED: + errno = EPERM; break; + case BOOTSTRAP_NO_MEMORY: + errno = ENOMEM; break; + default: + errno = EINVAL; break; + } + return -1; +} + +kern_return_t +mpm_wait(mach_port_t ajob, int *wstatus) +{ + return raw_mpm_wait(ajob, wstatus); +} + +kern_return_t +mpm_uncork_fork(mach_port_t ajob) +{ + return raw_mpm_uncork_fork(ajob); +} + +kern_return_t +bootstrap_create_server(mach_port_t bp, cmd_t server_cmd, uid_t server_uid, boolean_t on_demand, mach_port_t *server_port) +{ + return raw_bootstrap_create_server(bp, server_cmd, server_uid, on_demand, server_port); +} + +kern_return_t +bootstrap_subset(mach_port_t bp, mach_port_t requestor_port, mach_port_t *subset_port) +{ + return raw_bootstrap_subset(bp, requestor_port, subset_port); +} + +kern_return_t +bootstrap_unprivileged(mach_port_t bp, mach_port_t *unpriv_port) +{ + return raw_bootstrap_unprivileged(bp, unpriv_port); +} + +kern_return_t +bootstrap_getsocket(mach_port_t bp, name_t sockpath) +{ + return raw_bootstrap_getsocket(bp, sockpath); +} + +kern_return_t +bootstrap_parent(mach_port_t bp, mach_port_t *parent_port) +{ + return raw_bootstrap_parent(bp, parent_port); +} + +kern_return_t +bootstrap_register(mach_port_t bp, name_t service_name, mach_port_t sp) +{ + return raw_bootstrap_register(bp, service_name, sp); +} + +kern_return_t +bootstrap_create_service(mach_port_t bp, name_t service_name, mach_port_t *sp) +{ + return raw_bootstrap_create_service(bp, service_name, sp); +} + +kern_return_t +bootstrap_check_in(mach_port_t bp, name_t service_name, mach_port_t *sp) +{ + return raw_bootstrap_check_in(bp, service_name, sp); +} + +kern_return_t +bootstrap_look_up(mach_port_t bp, name_t service_name, mach_port_t *sp) +{ + return raw_bootstrap_look_up(bp, service_name, sp); +} + +kern_return_t +bootstrap_look_up_array(mach_port_t bp, + name_array_t names, mach_msg_type_number_t name_cnt, + mach_port_array_t *ports, mach_msg_type_number_t *port_cnt, + boolean_t *all) +{ + unsigned int i; + kern_return_t r; + + if (name_cnt > BOOTSTRAP_MAX_LOOKUP_COUNT) + return BOOTSTRAP_BAD_COUNT; + + *port_cnt = name_cnt; + + r = vm_allocate(mach_task_self(), (vm_address_t *)&ports, name_cnt * sizeof(mach_port_t), true); + + if (r != KERN_SUCCESS) + return r; + + *all = true; + + for (i = 0; i < name_cnt; i++) { + if (bootstrap_look_up(bp, names[i], &((*ports)[i])) == BOOTSTRAP_SUCCESS) + continue; + *all = false; + ports[i] = MACH_PORT_NULL; + } + + return BOOTSTRAP_SUCCESS; +} + +kern_return_t +bootstrap_status(mach_port_t bp, name_t service_name, bootstrap_status_t *service_active) +{ + mach_port_t p; + + if (bootstrap_check_in(bp, service_name, &p) == BOOTSTRAP_SUCCESS) { + mach_port_mod_refs(mach_task_self(), p, MACH_PORT_RIGHT_RECEIVE, -1); + *service_active = BOOTSTRAP_STATUS_ON_DEMAND; + if (raw_bootstrap_unprivileged(bp, &p) == BOOTSTRAP_SUCCESS) { + if (bp == p) + *service_active = BOOTSTRAP_STATUS_INACTIVE; + mach_port_deallocate(mach_task_self(), p); + } + return BOOTSTRAP_SUCCESS; + } else if (bootstrap_look_up(bp, service_name, &p) == BOOTSTRAP_SUCCESS) { + mach_port_deallocate(mach_task_self(), p); + *service_active = BOOTSTRAP_STATUS_ACTIVE; + return BOOTSTRAP_SUCCESS; + } + + return BOOTSTRAP_UNKNOWN_SERVICE; +} + +kern_return_t +bootstrap_info(mach_port_t bp, + name_array_t *service_names, mach_msg_type_number_t *service_namesCnt, + bootstrap_status_array_t *service_active, mach_msg_type_number_t *service_activeCnt) +{ + return raw_bootstrap_info(bp, service_names, service_namesCnt, + service_active, service_activeCnt); +} + +const char * +bootstrap_strerror(kern_return_t r) +{ + switch (r) { + case BOOTSTRAP_SUCCESS: + return "Success"; + case BOOTSTRAP_NOT_PRIVILEGED: + return "Permission denied"; + case BOOTSTRAP_NAME_IN_USE: + case BOOTSTRAP_SERVICE_ACTIVE: + return "Service name already exists"; + case BOOTSTRAP_UNKNOWN_SERVICE: + return "Unknown service name"; + case BOOTSTRAP_BAD_COUNT: + return "Too many lookups were requested in one request"; + case BOOTSTRAP_NO_MEMORY: + return "Out of memory"; + default: + return mach_error_string(r); + } +} diff --git a/launchd/src/bootstrap_public.h b/launchd/src/bootstrap_public.h new file mode 100644 index 0000000..bdd7485 --- /dev/null +++ b/launchd/src/bootstrap_public.h @@ -0,0 +1,321 @@ +#ifndef _BOOTSTRAP_H_ +#define _BOOTSTRAP_H_ +/* + * Copyright (c) 1999-2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_APACHE_LICENSE_HEADER_START@ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @APPLE_APACHE_LICENSE_HEADER_END@ + */ + +/* + * bootstrap -- fundamental service initiator and port server + * Mike DeMoney, NeXT, Inc. + * Copyright, 1990. All rights reserved. + */ + +/* + * Interface: Bootstrap server + * + * The bootstrap server is the first user-mode task initiated by the Mach + * kernel at system boot time. The bootstrap server provides two services, + * it initiates other system tasks, and manages a table of name-port bindings + * for fundamental system services (e.g. lookupd, Window Manager, etc...). + * + * Name-port bindings can be established with the bootstrap server by either + * of two mechanisms: + * + * 1. The binding can be indicated, in advance of the service that backs it + * being available, via a "service create" request. In this case, bootstrap + * will immediately create a port and bind the indicated name with that port. + * At a later time, a service may "checkin" for the name-port + * binding and will be returned receive rights for the bound port. Lookup's + * on bindings created by this mechanism will return send rights to the port, + * even if no service has "checked-in". In this case, requests sent to the + * bound port will be queued until a server has checked-in and can satisfy the + * request. + * + * 2. Bindings can be established dynamically via a "register" request. In + * this case, the register request provides bootstrap with a name and send + * rights for a port. Bootstrap will provide send rights for the bound port + * to any requestor via the lookup request. + * + * Bootstrap provides its service port to descendant tasks via the Mach + * "bootstrap" special task port. All direct descendants of bootstrap receive + * a "privileged" bootstrap service port. System services that initiate + * untrusted tasks should replace the Mach bootstrap task special port with + * a subset bootstrap port to prevent them from infecting the namespace. + * + * The bootstrap server creates a "backup" port for each service that it + * creates. This is used to detect when a checked out service is no longer + * being served. The bootstrap server regains all rights to the port and + * it is marked available for check-out again. This allows crashed servers to + * resume service to previous clients. Lookup's on this named port will + * continue to be serviced by bootstrap while holding receive rights for the + * bound port. A client may detect that the service is inactive via the + * bootstrap status request. If an inactive service re-registers rather + * than "checking-in" the original bound port is destroyed. + * + * The status of a named service may be obtained via the "status" request. + * A service is "active" if a name-port binding exists and receive rights + * to the bound port are held by a task other than bootstrap. + * + * The bootstrap server may also (re)start server processes associated with + * with a set of services. The definition of the server process is done + * through the "create server" request. The server will be launched in the + * same bootstrap context in which it was registered. + */ +#include +#include +#include +#include +#include +#include + +__BEGIN_DECLS + +#define BOOTSTRAP_MAX_NAME_LEN 128 +#define BOOTSTRAP_MAX_CMD_LEN 512 + +typedef char name_t[BOOTSTRAP_MAX_NAME_LEN]; +typedef char cmd_t[BOOTSTRAP_MAX_CMD_LEN]; +typedef name_t *name_array_t; +typedef int bootstrap_status_t; +typedef bootstrap_status_t *bootstrap_status_array_t; + +typedef boolean_t *bool_array_t; + +#define BOOTSTRAP_MAX_LOOKUP_COUNT 20 + +#define BOOTSTRAP_SUCCESS 0 +#define BOOTSTRAP_NOT_PRIVILEGED 1100 +#define BOOTSTRAP_NAME_IN_USE 1101 +#define BOOTSTRAP_UNKNOWN_SERVICE 1102 +#define BOOTSTRAP_SERVICE_ACTIVE 1103 +#define BOOTSTRAP_BAD_COUNT 1104 +#define BOOTSTRAP_NO_MEMORY 1105 + +#define BOOTSTRAP_STATUS_INACTIVE 0 +#define BOOTSTRAP_STATUS_ACTIVE 1 +#define BOOTSTRAP_STATUS_ON_DEMAND 2 + +/* + * After main() starts, it is safe to assume that this variable is always set. + */ +extern mach_port_t bootstrap_port; + +/* + * bootstrap_create_server() + * + * Declares a server that mach_init will re-spawn within the specified + * bootstrap context. The server is considered already "active" + * (i.e. will not be re-spawned) until the returned server_port is + * deallocated. + * + * In the meantime, services can be declared against the server, + * by using the server_port as the privileged bootstrap target of + * subsequent bootstrap_create_service() calls. + * + * When mach_init re-spawns the server, its task bootstrap port + * is set to the privileged sever_port. Through this special + * bootstrap port, it can access all of parent bootstrap's context + * (and all services are created in the parent's namespace). But + * all additional service declarations (and declaration removals) + * will be associated with this particular server. + * + * Only a holder of the server_port privilege bootstrap port can + * check in or register over those services. + * + * When all services associated with a server are deleted, and the server + * exits, it will automatically be deleted itself. + * + * If the server is declared "on_demand," then a non-running server + * will be re-launched on first use of one of the service ports + * registered against it. Otherwise, it will be re-launched + * immediately upon exiting (whether any client is actively using + * any of the service ports or not). + * + * Errors: Returns appropriate kernel errors on rpc failure. + * Returns BOOTSTRAP_NOT_PRIVILEGED, bootstrap or uid invalid. + */ +kern_return_t bootstrap_create_server( + mach_port_t bp, + cmd_t server_cmd, + uid_t server_uid, + boolean_t on_demand, + mach_port_t *server_port); + +/* + * bootstrap_subset() + * + * Returns a new port to use as a bootstrap port. This port behaves + * exactly like the previous bootstrap_port, except that ports dynamically + * registered via bootstrap_register() are available only to users of this + * specific subset_port. Lookups on the subset_port will return ports + * registered with this port specifically, and ports registered with + * ancestors of this subset_port. Duplications of services already + * registered with an ancestor port may be registered with the subset port + * are allowed. Services already advertised may then be effectively removed + * by registering PORT_NULL for the service. + * When it is detected that the requestor_port is destroyed the subset + * port and all services advertized by it are destroyed as well. + * + * Errors: Returns appropriate kernel errors on rpc failure. + */ +kern_return_t bootstrap_subset( + mach_port_t bp, + mach_port_t requestor_port, + mach_port_t *subset_port); + +/* + * bootstrap_unprivileged() + * + * Given a bootstrap port, return its unprivileged equivalent. If + * the port is already unprivileged, another reference to the same + * port is returned. + * + * This is most often used by servers, which are launched with their + * bootstrap port set to the privileged port for the server, to get + * an unprivileged version of the same port for use by its unprivileged + * children (or any offspring that it does not want to count as part + * of the "server" for mach_init registration and re-launch purposes). + * + * Native launchd jobs are always started with an unprivileged port. + */ +kern_return_t bootstrap_unprivileged( + mach_port_t bp, + mach_port_t *unpriv_port); + +/* + * bootstrap_parent() + * + * Given a bootstrap subset port, return the parent bootstrap port. + * If the specified bootstrap port is already the root subset, + * the same port will be returned. Much like "." and ".." are the same + * in the file system name space for the root directory ("/"). + * + * Errors: + * Returns BOOTSTRAP_NOT_PRIVILEGED if the caller is not running + * with an effective user id of root (as determined by the security + * token in the message trailer). + */ +kern_return_t bootstrap_parent( + mach_port_t bp, + mach_port_t *parent_port); + +/* + * bootstrap_register() + * + * Registers a send right for service_port with the service identified by + * service_name. Attempts to register a service where an active binding + * already exists are rejected. + * + * If the service was previously declared with bootstrap_create_service(), + * but is not currently active, this call can be used to undeclare the + * service. The bootstrap port used must have sufficient privilege to + * do so. (Registering MACH_PORT_NULL is especially useful for shutting + * down declared services). + * + * Errors: Returns appropriate kernel errors on rpc failure. + * Returns BOOTSTRAP_NOT_PRIVILEGED, if request directed to + * bootstrap port without privilege. + * Returns BOOTSTRAP_NAME_IN_USE, if service has already been + * register or checked-in. + */ +kern_return_t bootstrap_register( + mach_port_t bp, + name_t service_name, + mach_port_t sp); + +/* + * bootstrap_create_service() + * + * Creates a service named "service_name" and returns send rights to that + * port in "service_port." The port may later be checked in as if this + * port were configured in the bootstrap configuration file. + * + * Errors: Returns appropriate kernel errors on rpc failure. + * Returns BOOTSTRAP_SERVICE_ACTIVE, if service already exists. + */ +kern_return_t bootstrap_create_service( + mach_port_t bp, + name_t service_name, + mach_port_t *sp); + +/* + * bootstrap_check_in() + * + * Returns the receive right for the service named by service_name. The + * service must have previously been declared in this bootstrap context via + * a call to bootstrap_create_service(). Attempts to check_in a service + * which is already active are not allowed. + * + * If the service was declared as being associated with a server, the + * check_in must come from the server's privileged port (server_port). + * + * Errors: Returns appropriate kernel errors on rpc failure. + * Returns BOOTSTRAP_UNKNOWN_SERVICE, if service does not exist. + * Returns BOOTSTRAP_NOT_PRIVILEGED, if request directed to + * bootstrap port without privilege. + * Returns BOOTSTRAP_SERVICE_ACTIVE, if service has already been + * registered or checked-in. + */ +kern_return_t bootstrap_check_in( + mach_port_t bp, + name_t service_name, + mach_port_t *sp); + +/* + * bootstrap_look_up() + * + * Returns a send right for the service port declared/registered under the + * name service_name. The service is not guaranteed to be active. Use the + * bootstrap_status call to determine the status of the service. + * + * Errors: Returns appropriate kernel errors on rpc failure. + * Returns BOOTSTRAP_UNKNOWN_SERVICE, if service does not exist. + */ +kern_return_t bootstrap_look_up( + mach_port_t bp, + name_t service_name, + mach_port_t *sp); + +/* + * bootstrap_status() + * + * In practice, this call was used to preflight whether the following two + * APIs would succeed. + * + * bootstrap_look_up() + * bootstrap_check_in() + * + * Please don't bother. Just call the above two APIs directly and check + * for failure. + */ +kern_return_t bootstrap_status( + mach_port_t bp, + name_t service_name, + bootstrap_status_t *service_active) + AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_5; + +/* bootstrap_strerror() + * + * Translate a return value from the bootstrap_*() APIs to a string. + */ +const char *bootstrap_strerror(kern_return_t r) __attribute__((__nothrow__, __pure__, __warn_unused_result__)); + +__END_DECLS + +#endif diff --git a/launchd/src/hostconfig b/launchd/src/hostconfig index 0f7fac0..9855845 100644 --- a/launchd/src/hostconfig +++ b/launchd/src/hostconfig @@ -1,7 +1,8 @@ +# This file is going away + AFPSERVER=-NO- AUTHSERVER=-NO- AUTOMOUNT=-YES- -CUPS=-AUTOMATIC- NFSLOCKS=-AUTOMATIC- NISDOMAIN=-NO- TIMESYNC=-NO- @@ -9,4 +10,3 @@ QTSSERVER=-NO- WEBSERVER=-NO- SMBSERVER=-NO- SNMPSERVER=-NO- -SPOTLIGHT=-YES- diff --git a/launchd/src/init.8 b/launchd/src/init.8 deleted file mode 100644 index 8db766d..0000000 --- a/launchd/src/init.8 +++ /dev/null @@ -1,295 +0,0 @@ -.\" Copyright (c) 1980, 1991, 1993 -.\" The Regents of the University of California. All rights reserved. -.\" -.\" This code is derived from software contributed to Berkeley by -.\" Donn Seeley at Berkeley Software Design, Inc. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software developed by the University of -.\" California, Berkeley and its contributors. -.\" 4. Neither the name of the University nor the names of its contributors -.\" may be used to endorse or promote products derived from this software -.\" without specific prior written permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -.\" SUCH DAMAGE. -.\" -.\" @(#)init.8 8.6 (Berkeley) 5/26/95 -.\" -.Dd May 26, 1995 -.Dt INIT 8 -.Os BSD 4 -.Sh NAME -.Nm init -.Nd process control initialization -.Sh SYNOPSIS -.Nm init -.Sh DESCRIPTION -The -.Nm init -program -is the last stage of the boot process. -It normally runs the automatic reboot sequence as described in -.Xr reboot 8 , -and if this succeeds, begins multi-user operation. -If the reboot scripts fail, -.Nm init -commences single user operation by giving -the super-user a shell on the console. -The -.Nm init -program may be passed parameters -from the boot program to -prevent the system from going multi-user and to instead execute -a single user shell without starting the normal daemons. -The system is then quiescent for maintenance work and may -later be made to go to multi-user by exiting the -single-user shell (with ^D). -This -causes -.Nm init -to run the -.Pa /etc/rc -start up command file in fastboot mode (skipping disk checks). -.Pp -If the -.Nm console -entry in the -.Xr ttys 5 -file is marked ``insecure'', -then -.Nm init -will require that the superuser password be -entered before the system will start a single-user shell. -The password check is skipped if the -.Nm console -is marked as ``secure''. -.Pp -The kernel runs with four different levels of security. -Any superuser process can raise the security level, but only -.Nm init -can lower it. -Security levels are defined as follows: -.Bl -tag -width flag -.It Ic -1 -Permanently insecure mode \- always run system in level 0 mode. -.It Ic 0 -Insecure mode \- immutable and append-only flags may be turned off. -All devices may be read or written subject to their permissions. -.It Ic 1 -Secure mode \- immutable and append-only flags may not be changed; -disks for mounted filesystems, -.Pa /dev/mem , -and -.Pa /dev/kmem -are read-only. -The -.Xr settimeofday 2 -system call can only advance the time. -.It Ic 2 -Highly secure mode \- same as secure mode, plus disks are always -read-only whether mounted or not. -This level precludes tampering with filesystems by unmounting them, -but also inhibits running -.Xr newfs 8 -while the system is multi-user. -.El -.Pp -Normally, the system runs in level 0 mode while single user -and in level 1 mode while multiuser. -If the level 2 mode is desired while running multiuser, -it can be set in the startup script -.Pa /etc/rc -using -.Xr sysctl 8 . -If it is desired to run the system in level 0 mode while multiuser, -the administrator must build a kernel with the variable -.Nm securelevel -defined in the file -.Pa /sys/compile/MACHINE/param.c -and initialize it to -1. -.Pp -In multi-user operation, -.Nm init -maintains -processes for the terminal ports found in the file -.Xr ttys 5 . -.Nm Init -reads this file, and executes the command found in the second field. -This command is usually -.Xr getty 8 ; -.Xr getty -opens and initializes the tty line -and -executes the -.Xr login -program. -The -.Xr login -program, when a valid user logs in, -executes a shell for that user. When this shell -dies, either because the user logged out -or an abnormal termination occurred (a signal), -the -.Nm init -program wakes up, deletes the user -from the -.Xr utmp 5 -file of current users and records the logout in the -.Xr wtmp -file. -The cycle is -then restarted by -.Nm init -executing a new -.Xr getty -for the line. -.pl +1 -.Pp -Line status (on, off, secure, getty, or window information) -may be changed in the -.Xr ttys -file without a reboot by sending the signal -.Dv SIGHUP -to -.Nm init -with the command -.Dq Li "kill \-s HUP 1" . -On receipt of this signal, -.Nm init -re-reads the -.Xr ttys -file. -When a line is turned off in -.Xr ttys , -.Nm init -will send a SIGHUP signal to the controlling process -for the session associated with the line. -For any lines that were previously turned off in the -.Xr ttys -file and are now on, -.Nm init -executes a new -.Xr getty -to enable a new login. -If the getty or window field for a line is changed, -the change takes effect at the end of the current -login session (e.g., the next time -.Nm init -starts a process on the line). -If a line is commented out or deleted from -.Xr ttys , -.Nm init -will not do anything at all to that line. -However, it will complain that the relationship between lines -in the -.Xr ttys -file and records in the -.Xr utmp -file is out of sync, -so this practice is not recommended. -.Pp -.Nm Init -will terminate multi-user operations and resume single-user mode -if sent a terminate -.Pq Dv TERM -signal, for example, -.Dq Li "kill \-s TERM 1" . -If there are processes outstanding that are deadlocked (because of -hardware or software failure), -.Xr init -will not wait for them all to die (which might take forever), but -will time out after 30 seconds and print a warning message. -.Pp -.Nm Init -will cease creating new -.Xr getty Ns 's -and allow the system to slowly die away, if it is sent a terminal stop -.Pq Dv TSTP -signal, i.e. -.Dq Li "kill \-s TSTP 1" . -A later hangup will resume full -multi-user operations, or a terminate will start a single user shell. -This hook is used by -.Xr reboot 8 -and -.Xr halt 8 . -.Pp -The role of -.Nm init -is so critical that if it dies, the system will reboot itself -automatically. -If, at bootstrap time, the -.Xr init -process cannot be located, the system will panic with the message -``panic: "init died (signal %d, exit %d)''. -.Sh DIAGNOSTICS -.Bl -diag -.It "getty repeating too quickly on port %s, sleeping" -A process being started to service a line is exiting quickly -each time it is started. -This is often caused by a ringing or noisy terminal line. -.Em "Init will sleep for 10 seconds" , -.Em "then continue trying to start the process" . -.Pp -.It "some processes would not die; ps axl advised." -A process -is hung and could not be killed when the system was shutting down. -This condition is usually caused by a process -that is stuck in a device driver because of -a persistent device error condition. -.El -.Sh FILES -.Bl -tag -width /var/log/wtmp -compact -.It Pa /dev/console -System console device. -.It Pa /dev/tty* -Terminal ports found in -.Xr ttys . -.It Pa /var/run/utmp -Record of Current users on the system. -.It Pa /var/log/wtmp -Record of all logins and logouts. -.It Pa /etc/ttys -The terminal initialization information file. -.It Pa /etc/rc -System startup commands. -.El -.Sh SEE ALSO -.Xr login 1 , -.Xr kill 1 , -.Xr sh 1 , -.Xr ttys 5 , -.Xr crash 8 , -.Xr getty 8 , -.Xr rc 8 , -.Xr reboot 8 , -.Xr halt 8 , -.Xr shutdown 8 -.Sh HISTORY -A -.Nm -command appeared in -.At v6 . -.Sh BUGS -Systems without -.Xr sysctl -behave as though they have security level \-1. diff --git a/launchd/src/init.c b/launchd/src/init.c index 05abab3..83be58a 100644 --- a/launchd/src/init.c +++ b/launchd/src/init.c @@ -1,25 +1,21 @@ /* * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ + * @APPLE_APACHE_LICENSE_HEADER_START@ * - * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights - * Reserved. This file contains Original Code and/or Modifications of - * Original Code as defined in and that are subject to the Apple Public - * Source License Version 1.0 (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. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * The 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." + * http://www.apache.org/licenses/LICENSE-2.0 * - * @APPLE_LICENSE_HEADER_END@ + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @APPLE_APACHE_LICENSE_HEADER_END@ */ /*- * Copyright (c) 1991, 1993 @@ -57,15 +53,16 @@ * SUCH DAMAGE. */ +static const char *const __rcs_file_version__ = "$Revision: 1.38 $"; + #include #include #include -#include - #include #include #include +#include #include #include #include @@ -90,7 +87,6 @@ #include #include "launchd.h" -#include "bootstrap_internal.h" #define _PATH_RUNCOM "/etc/rc" @@ -113,9 +109,7 @@ static kq_callback kqruncom_callback = runcom_callback; static void single_user(void); static void runcom(void); -static bool runcom_verbose = false; static bool runcom_safe = false; -static bool runcom_fsck = true; static bool runcom_netboot = false; static bool single_user_mode = false; static bool run_runcom = true; @@ -176,37 +170,28 @@ static char **construct_argv(char *); static void setsecuritylevel(int); static int getsecuritylevel(void); static int setupargv(session_t, struct ttyent *); +static bool should_fsck(void); void -init_boot(bool sflag, bool vflag, bool xflag) +init_boot(bool sflag) { int nbmib[2] = { CTL_KERN, KERN_NETBOOT }; - uint64_t nb = 0; - size_t nbsz = sizeof(nb); + int sbmib[2] = { CTL_KERN, KERN_SAFEBOOT }; + uint32_t v = 0; + size_t vsz = sizeof(v); if (sflag) { single_user_mode = true; run_runcom = false; } - if (vflag) - runcom_verbose = true; - if (xflag) - runcom_safe = true; - - if (sysctl(nbmib, 2, &nb, &nbsz, NULL, 0) == 0) { - /* The following assignment of nb to itself if the size of data - * returned is 32 bits instead of 64 is a clever C trick to - * move the 32 bits on big endian systems to the least - * significant bytes of the 64 mem variable. - * - * On little endian systems, this is effectively a no-op. - */ - if (nbsz == 4) - nb = *(uint32_t *)&nb; - if (nb != 0) + + if (launchd_assumes(sysctl(nbmib, 2, &v, &vsz, NULL, 0) != -1)) { + if (v != 0) runcom_netboot = true; - } else { - syslog(LOG_WARNING, "sysctl(\"kern.netboot\") %m"); + } + if (launchd_assumes(sysctl(sbmib, 2, &v, &vsz, NULL, 0) != -1)) { + if (v != 0) + runcom_safe = true; } } @@ -216,26 +201,27 @@ init_pre_kevent(void) { session_t s; - if (single_user_mode && single_user_pid == 0) - single_user(); + if (single_user_pid || runcom_pid) + return; + + if (single_user_mode) + return single_user(); if (run_runcom) - runcom(); + return runcom(); - if (!single_user_mode && !run_runcom && runcom_pid == 0) { - /* - * If the administrator has not set the security level to -1 - * to indicate that the kernel should not run multiuser in secure - * mode, and the run script has not set a higher level of security - * than level 1, then put the kernel into secure mode. - */ - if (getsecuritylevel() == 0) - setsecuritylevel(1); - - TAILQ_FOREACH(s, &sessions, tqe) { - if (s->se_process == 0) - session_launch(s); - } + /* + * If the administrator has not set the security level to -1 + * to indicate that the kernel should not run multiuser in secure + * mode, and the run script has not set a higher level of security + * than level 1, then put the kernel into secure mode. + */ + if (getsecuritylevel() == 0) + setsecuritylevel(1); + + TAILQ_FOREACH(s, &sessions, tqe) { + if (s->se_process == 0) + session_launch(s); } } @@ -308,12 +294,13 @@ setctty(const char *name, int flags) static void single_user(void) { + bool runcom_fsck = should_fsck(); char *argv[2]; if (getsecuritylevel() > 0) setsecuritylevel(0); - if ((single_user_pid = fork_with_bootstrap_port(launchd_bootstrap_port)) == -1) { + if ((single_user_pid = launchd_fork()) == -1) { syslog(LOG_ERR, "can't fork single-user shell, trying again: %m"); return; } else if (single_user_pid == 0) { @@ -330,20 +317,18 @@ single_user(void) fprintf(stdout, "Root device is mounted read-only\n\n"); fprintf(stdout, "If you want to make modifications to files:\n"); fprintf(stdout, "\t/sbin/fsck -fy\n\t/sbin/mount -uw /\n\n"); - fprintf(stdout, "If you wish to boot the system, but stay in single user mode:\n"); - fprintf(stdout, "\tsh /etc/rc\n"); + fprintf(stdout, "If you wish to boot the system:\n"); + fprintf(stdout, "\texit\n\n"); fflush(stdout); } argv[0] = "-sh"; argv[1] = NULL; - setpriority(PRIO_PROCESS, 0, 0); execv(_PATH_BSHELL, argv); syslog(LOG_ERR, "can't exec %s for single user: %m", _PATH_BSHELL); sleep(STALL_TIMEOUT); exit(EXIT_FAILURE); } else { - runcom_fsck = false; if (kevent_mod(single_user_pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, &kqsingle_user_callback) == -1) single_user_callback(NULL, NULL); @@ -353,21 +338,10 @@ single_user(void) static void single_user_callback(void *obj __attribute__((unused)), struct kevent *kev __attribute__((unused))) { - int status, r = single_user_pid; - -#ifdef PID1_REAP_ADOPTED_CHILDREN - status = pid1_child_exit_status; -#else - r = waitpid(single_user_pid, &status, 0); -#endif - - if (r != single_user_pid) { - if (r == -1) - syslog(LOG_ERR, "single_user_callback(): waitpid(): %m"); - if (r == 0) - syslog(LOG_ERR, "single_user_callback(): waitpid() returned 0"); + int status; + + if (!launchd_assumes(waitpid(single_user_pid, &status, 0) == single_user_pid)) return; - } if (WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS) { syslog(LOG_INFO, "single user shell terminated, restarting"); @@ -390,13 +364,14 @@ static struct timeval runcom_start_tv = { 0, 0 }; static void runcom(void) { - char *argv[3]; + bool runcom_fsck = should_fsck(); + char *argv[] = { "/bin/launchctl", "bootstrap", NULL }; struct termios term; int vdisable; gettimeofday(&runcom_start_tv, NULL); - if ((runcom_pid = fork_with_bootstrap_port(launchd_bootstrap_port)) == -1) { + if ((runcom_pid = launchd_fork()) == -1) { syslog(LOG_ERR, "can't fork for %s on %s: %m", _PATH_BSHELL, _PATH_RUNCOM); sleep(STALL_TIMEOUT); runcom_pid = 0; @@ -404,7 +379,6 @@ runcom(void) return; } else if (runcom_pid > 0) { run_runcom = false; - runcom_fsck = false; if (kevent_mod(runcom_pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, &kqruncom_callback) == -1) { runcom_callback(NULL, NULL); @@ -431,17 +405,11 @@ runcom(void) syslog(LOG_WARNING, "tcsetattr(\"%s\") %m", _PATH_CONSOLE); } - argv[0] = "sh"; - argv[1] = _PATH_RUNCOM; - argv[2] = NULL; - setenv("SafeBoot", runcom_safe ? "-x" : "", 1); - setenv("VerboseFlag", runcom_verbose ? "-v" : "", 1); setenv("FsckSlash", runcom_fsck ? "-F" : "", 1); setenv("NetBoot", runcom_netboot ? "-N" : "", 1); - setpriority(PRIO_PROCESS, 0, 0); - execv(_PATH_BSHELL, argv); + execv(argv[0], argv); stall("can't exec %s for %s: %m", _PATH_BSHELL, _PATH_RUNCOM); exit(EXIT_FAILURE); } @@ -452,7 +420,6 @@ runcom_callback(void *obj __attribute__((unused)), struct kevent *kev __attribut int status; struct timeval runcom_end_tv, runcom_total_tv; double sec; - pid_t r = runcom_pid; gettimeofday(&runcom_end_tv, NULL); timersub(&runcom_end_tv, &runcom_start_tv, &runcom_total_tv); @@ -460,19 +427,9 @@ runcom_callback(void *obj __attribute__((unused)), struct kevent *kev __attribut sec += (double)runcom_total_tv.tv_usec / (double)1000000; syslog(LOG_INFO, "%s finished in: %.3f seconds", _PATH_RUNCOM, sec); -#ifdef PID1_REAP_ADOPTED_CHILDREN - status = pid1_child_exit_status; -#else - r = waitpid(runcom_pid, &status, 0); -#endif - - if (r == runcom_pid) { + if (launchd_assumes(waitpid(runcom_pid, &status, 0) == runcom_pid)) { runcom_pid = 0; } else { - if (r == -1) - syslog(LOG_ERR, "waitpid() for '%s %s' failed: %m", _PATH_BSHELL, _PATH_RUNCOM); - if (r == 0) - syslog(LOG_ERR, "waitpid() for '%s %s' returned 0", _PATH_BSHELL, _PATH_RUNCOM); syslog(LOG_ERR, "going to single user mode"); single_user_mode = true; return; @@ -480,7 +437,6 @@ runcom_callback(void *obj __attribute__((unused)), struct kevent *kev __attribut if (WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS) { logwtmp("~", "reboot", ""); - update_ttys(); return; } else if (WIFSIGNALED(status) && (WTERMSIG(status) == SIGTERM || WTERMSIG(status) == SIGKILL)) { return; @@ -632,6 +588,7 @@ session_launch(session_t s) se_cmd_t *se_cmd; const char *session_type = NULL; time_t current_time = time(NULL); + bool is_loginwindow = false; // Setup the default values; switch (s->se_flags & SE_GETTY_LAUNCH) { @@ -656,11 +613,14 @@ session_launch(session_t s) break; } - /* fork(), not vfork() -- we can't afford to block. */ - if ((pid = fork_with_bootstrap_port(launchd_bootstrap_port)) == -1) { + if (strcmp(se_cmd->argv[0], "/System/Library/CoreServices/loginwindow.app/Contents/MacOS/loginwindow") == 0) + is_loginwindow = true; + + pid = launchd_fork(); + + if (pid == -1) { syslog(LOG_ERR, "can't fork for %s on port %s: %m", session_type, s->se_device); - update_ttys(); return; } @@ -683,10 +643,9 @@ session_launch(session_t s) sigemptyset(&mask); sigprocmask(SIG_SETMASK, &mask, NULL); - setpriority(PRIO_PROCESS, 0, 0); - if (strcmp(se_cmd->argv[0], "/System/Library/CoreServices/loginwindow.app/Contents/MacOS/loginwindow")) - launchd_SessionCreate(se_cmd->argv[0]); + if (!is_loginwindow) + launchd_SessionCreate(); execv(se_cmd->argv[0], se_cmd->argv); stall("can't exec %s '%s' for port %s: %m", session_type, @@ -710,40 +669,28 @@ session_callback(void *obj, struct kevent *kev __attribute__((unused))) static void session_reap(session_t s) { - pid_t pr = s->se_process; char *line; int status; -#ifdef PID1_REAP_ADOPTED_CHILDREN - status = pid1_child_exit_status; -#else - pr = waitpid(s->se_process, &status, 0); -#endif - - switch (pr) { - case -1: - syslog(LOG_ERR, "waitpid(): %m"); - return; - case 0: - syslog(LOG_ERR, "waitpid() == 0"); + if (!launchd_assumes(waitpid(s->se_process, &status, 0) == s->se_process)) return; - default: - if (WIFSIGNALED(status)) { - syslog(LOG_WARNING, "%s port %s exited abnormally: %s", - s->se_getty.path, s->se_device, strsignal(WTERMSIG(status))); - s->se_flags |= SE_ONERROR; - } else if (WEXITSTATUS(status) == REALLY_EXIT_TO_CONSOLE) { - /* WIFEXITED(status) assumed */ - s->se_flags |= SE_ONOPTION; - } else { - s->se_flags |= SE_ONERROR; - } - s->se_process = 0; - line = s->se_device + sizeof(_PATH_DEV) - 1; - if (logout(line)) - logwtmp(line, "", ""); - break; + + if (WIFSIGNALED(status)) { + syslog(LOG_WARNING, "%s port %s exited abnormally: %s", + s->se_getty.path, s->se_device, strsignal(WTERMSIG(status))); + s->se_flags |= SE_ONERROR; + } else if (WEXITSTATUS(status) == REALLY_EXIT_TO_CONSOLE) { + /* WIFEXITED(status) assumed */ + s->se_flags |= SE_ONOPTION; + } else { + s->se_flags |= SE_ONERROR; } + + s->se_process = 0; + line = s->se_device + sizeof(_PATH_DEV) - 1; + + if (logout(line)) + logwtmp(line, "", ""); } /* @@ -808,53 +755,35 @@ catatonia(void) s->se_flags |= SE_SHUTDOWN; } -/* - * Bring the system down to single user. - */ -void -death(void) +bool init_check_pid(pid_t p) { - int i; - static const int death_sigs[3] = { SIGHUP, SIGTERM, SIGKILL }; - - catatonia(); + session_t s; - single_user_mode = true; + TAILQ_FOREACH(s, &sessions, tqe) { + if (s->se_process == p) + return true; + } - /* NB: should send a message to the session logger to avoid blocking. */ - logwtmp("~", "shutdown", ""); + if (single_user_pid == p) + return true; - for (i = 0; i < 3; ++i) { - if (kill(-1, death_sigs[i]) == -1 && errno == ESRCH) - return; - syslog(LOG_ERR, "we should be trying to detect a valid clean-up"); - sleep(DEATH_WATCH); - } + if (runcom_pid == p) + return true; - syslog(LOG_WARNING, "some processes would not die; ps axl advised"); + return false; } -#ifdef PID1_REAP_ADOPTED_CHILDREN -__private_extern__ bool init_check_pid(pid_t p) +bool +should_fsck(void) { - struct kevent kev; - session_t s; + struct statfs sfs; + bool r = true; - TAILQ_FOREACH(s, &sessions, tqe) { - if (s->se_process == p) { - EV_SET(&kev, p, EVFILT_PROC, 0, 0, 0, s); - s->se_callback(s, &kev); - return true; + if (launchd_assumes(statfs("/", &sfs) != -1)) { + if (!(sfs.f_flags & MNT_RDONLY)) { + r = false; } } - if (single_user_pid == p) { - single_user_callback(NULL, NULL); - return true; - } - if (runcom_pid == p) { - runcom_callback(NULL, NULL); - return true; - } - return false; + + return r; } -#endif diff --git a/launchd/src/launch.h b/launchd/src/launch.h index 1d93ad9..38b5d8d 100644 --- a/launchd/src/launch.h +++ b/launchd/src/launch.h @@ -1,31 +1,46 @@ /* * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ + * @APPLE_APACHE_LICENSE_HEADER_START@ * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * The 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, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and * limitations under the License. * - * @APPLE_LICENSE_HEADER_END@ + * @APPLE_APACHE_LICENSE_HEADER_END@ */ #ifndef _LAUNCH_H_ #define _LAUNCH_H_ +#include +#include #include #include +__BEGIN_DECLS + +#ifdef __GNUC__ +#define __ld_normal __attribute__((__nothrow__)) +#define __ld_setter __attribute__((__nothrow__, __nonnull__)) +#define __ld_getter __attribute__((__nothrow__, __nonnull__, __pure__, __warn_unused_result__)) +#define __ld_iterator(x, y) __attribute__((__nonnull__(x, y))) +#define __ld_allocator __attribute__((__nothrow__, __malloc__, __nonnull__, __warn_unused_result__)) +#else +#define __ld_normal +#define __ld_setter +#define __ld_getter +#define __ld_iterator(x, y) +#define __ld_allocator +#endif + #define LAUNCH_KEY_SUBMITJOB "SubmitJob" #define LAUNCH_KEY_REMOVEJOB "RemoveJob" @@ -43,36 +58,51 @@ #define LAUNCH_JOBKEY_TIMEOUT "TimeOut" #define LAUNCH_JOBKEY_INITGROUPS "InitGroups" #define LAUNCH_JOBKEY_SOCKETS "Sockets" -#define LAUNCH_JOBKEY_EVENTSOURCES "EventSources" +#define LAUNCH_JOBKEY_MACHSERVICES "MachServices" #define LAUNCH_JOBKEY_INETDCOMPATIBILITY "inetdCompatibility" +#define LAUNCH_JOBKEY_ENABLEGLOBBING "EnableGlobbing" #define LAUNCH_JOBKEY_PROGRAMARGUMENTS "ProgramArguments" #define LAUNCH_JOBKEY_PROGRAM "Program" #define LAUNCH_JOBKEY_ONDEMAND "OnDemand" +#define LAUNCH_JOBKEY_KEEPALIVE "KeepAlive" +#define LAUNCH_JOBKEY_LIMITLOADTOHOSTS "LimitLoadToHosts" +#define LAUNCH_JOBKEY_LIMITLOADFROMHOSTS "LimitLoadFromHosts" +#define LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE "LimitLoadToSessionType" #define LAUNCH_JOBKEY_RUNATLOAD "RunAtLoad" #define LAUNCH_JOBKEY_ROOTDIRECTORY "RootDirectory" #define LAUNCH_JOBKEY_WORKINGDIRECTORY "WorkingDirectory" #define LAUNCH_JOBKEY_SERVICEDESCRIPTION "ServiceDescription" #define LAUNCH_JOBKEY_ENVIRONMENTVARIABLES "EnvironmentVariables" #define LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES "UserEnvironmentVariables" -#define LAUNCH_JOBKEY_PID "PID" +#define LAUNCH_JOBKEY_FORCEPOWERPC "ForcePowerPC" #define LAUNCH_JOBKEY_UMASK "Umask" #define LAUNCH_JOBKEY_NICE "Nice" #define LAUNCH_JOBKEY_LOWPRIORITYIO "LowPriorityIO" #define LAUNCH_JOBKEY_SESSIONCREATE "SessionCreate" #define LAUNCH_JOBKEY_SOFTRESOURCELIMITS "SoftResourceLimits" #define LAUNCH_JOBKEY_HARDRESOURCELIMITS "HardResourceLimits" -#define LAUNCH_JOBKEY_SERVICEIPC "ServiceIPC" #define LAUNCH_JOBKEY_STANDARDOUTPATH "StandardOutPath" #define LAUNCH_JOBKEY_STANDARDERRORPATH "StandardErrorPath" #define LAUNCH_JOBKEY_DEBUG "Debug" +#define LAUNCH_JOBKEY_WAITFORDEBUGGER "WaitForDebugger" #define LAUNCH_JOBKEY_QUEUEDIRECTORIES "QueueDirectories" #define LAUNCH_JOBKEY_WATCHPATHS "WatchPaths" #define LAUNCH_JOBKEY_STARTINTERVAL "StartInterval" #define LAUNCH_JOBKEY_STARTCALENDARINTERVAL "StartCalendarInterval" #define LAUNCH_JOBKEY_BONJOURFDS "BonjourFDs" +#define LAUNCH_JOBKEY_LASTEXITSTATUS "LastExitStatus" +#define LAUNCH_JOBKEY_PID "PID" +#define LAUNCH_JOBKEY_SUBJOBS "SubJobs" #define LAUNCH_JOBINETDCOMPATIBILITY_WAIT "Wait" +#define LAUNCH_JOBKEY_MACH_RESETATCLOSE "ResetAtClose" +#define LAUNCH_JOBKEY_MACH_HIDEUNTILCHECKIN "HideUntilCheckIn" + +#define LAUNCH_JOBKEY_KEEPALIVE_SUCCESSFULEXIT "SuccessfulExit" +#define LAUNCH_JOBKEY_KEEPALIVE_NETWORKSTATE "NetworkState" +#define LAUNCH_JOBKEY_KEEPALIVE_PATHSTATE "PathState" + #define LAUNCH_JOBKEY_CAL_MINUTE "Minute" #define LAUNCH_JOBKEY_CAL_HOUR "Hour" #define LAUNCH_JOBKEY_CAL_DAY "Day" @@ -113,48 +143,52 @@ typedef enum { LAUNCH_DATA_STRING, LAUNCH_DATA_OPAQUE, LAUNCH_DATA_ERRNO, + LAUNCH_DATA_MACHPORT, } launch_data_type_t; -launch_data_t launch_data_alloc(launch_data_type_t); -launch_data_t launch_data_copy(launch_data_t); -launch_data_type_t launch_data_get_type(launch_data_t); -void launch_data_free(launch_data_t); +launch_data_t launch_data_alloc(launch_data_type_t) __ld_allocator; +launch_data_t launch_data_copy(launch_data_t) __ld_allocator; +launch_data_type_t launch_data_get_type(const launch_data_t) __ld_getter; +void launch_data_free(launch_data_t) __ld_setter; /* Generic Dictionaries */ /* the value should not be changed while iterating */ -bool launch_data_dict_insert(launch_data_t, launch_data_t, const char *); -launch_data_t launch_data_dict_lookup(launch_data_t, const char *); -bool launch_data_dict_remove(launch_data_t, const char *); -void launch_data_dict_iterate(launch_data_t, void (*)(launch_data_t, const char *, void *), void *); -size_t launch_data_dict_get_count(launch_data_t); +bool launch_data_dict_insert(launch_data_t, const launch_data_t, const char *) __ld_setter; +launch_data_t launch_data_dict_lookup(const launch_data_t, const char *) __ld_getter; +bool launch_data_dict_remove(launch_data_t, const char *) __ld_setter; +void launch_data_dict_iterate(const launch_data_t, void (*)(const launch_data_t, const char *, void *), void *) __ld_iterator(1, 2); +size_t launch_data_dict_get_count(const launch_data_t) __ld_getter; /* Generic Arrays */ -bool launch_data_array_set_index(launch_data_t, launch_data_t, size_t); -launch_data_t launch_data_array_get_index(launch_data_t, size_t); -size_t launch_data_array_get_count(launch_data_t); - -launch_data_t launch_data_new_fd(int); -launch_data_t launch_data_new_integer(long long); -launch_data_t launch_data_new_bool(bool); -launch_data_t launch_data_new_real(double); -launch_data_t launch_data_new_string(const char *); -launch_data_t launch_data_new_opaque(const void *, size_t); - -bool launch_data_set_fd(launch_data_t, int); -bool launch_data_set_integer(launch_data_t, long long); -bool launch_data_set_bool(launch_data_t, bool); -bool launch_data_set_real(launch_data_t, double); -bool launch_data_set_string(launch_data_t, const char *); -bool launch_data_set_opaque(launch_data_t, const void *, size_t); - -int launch_data_get_fd(launch_data_t); -long long launch_data_get_integer(launch_data_t); -bool launch_data_get_bool(launch_data_t); -double launch_data_get_real(launch_data_t); -const char * launch_data_get_string(launch_data_t); -void * launch_data_get_opaque(launch_data_t); -size_t launch_data_get_opaque_size(launch_data_t); -int launch_data_get_errno(launch_data_t); +bool launch_data_array_set_index(launch_data_t, const launch_data_t, size_t) __ld_setter; +launch_data_t launch_data_array_get_index(const launch_data_t, size_t) __ld_getter; +size_t launch_data_array_get_count(const launch_data_t) __ld_getter; + +launch_data_t launch_data_new_fd(int) __ld_allocator; +launch_data_t launch_data_new_machport(mach_port_t) __ld_allocator; +launch_data_t launch_data_new_integer(long long) __ld_allocator; +launch_data_t launch_data_new_bool(bool) __ld_allocator; +launch_data_t launch_data_new_real(double) __ld_allocator; +launch_data_t launch_data_new_string(const char *) __ld_allocator; +launch_data_t launch_data_new_opaque(const void *, size_t) __ld_allocator; + +bool launch_data_set_fd(launch_data_t, int) __ld_setter; +bool launch_data_set_machport(launch_data_t, mach_port_t) __ld_setter; +bool launch_data_set_integer(launch_data_t, long long) __ld_setter; +bool launch_data_set_bool(launch_data_t, bool) __ld_setter; +bool launch_data_set_real(launch_data_t, double) __ld_setter; +bool launch_data_set_string(launch_data_t, const char *) __ld_setter; +bool launch_data_set_opaque(launch_data_t, const void *, size_t) __ld_setter; + +int launch_data_get_fd(const launch_data_t) __ld_getter; +mach_port_t launch_data_get_machport(const launch_data_t) __ld_getter; +long long launch_data_get_integer(const launch_data_t) __ld_getter; +bool launch_data_get_bool(const launch_data_t) __ld_getter; +double launch_data_get_real(const launch_data_t) __ld_getter; +const char * launch_data_get_string(const launch_data_t) __ld_getter; +void * launch_data_get_opaque(const launch_data_t) __ld_getter; +size_t launch_data_get_opaque_size(const launch_data_t) __ld_getter; +int launch_data_get_errno(const launch_data_t) __ld_getter; /* launch_get_fd() @@ -162,7 +196,7 @@ int launch_data_get_errno(launch_data_t); * Use this to get the FD if you're doing asynchronous I/O with select(), * poll() or kevent(). */ -int launch_get_fd(void); +int launch_get_fd(void) __ld_normal; /* launch_msg() * @@ -175,6 +209,8 @@ int launch_get_fd(void); * If no messages were to be sent, it returns NULL and errno is set to zero if * no more asynchronous messages are available. */ -launch_data_t launch_msg(launch_data_t); +launch_data_t launch_msg(const launch_data_t) __ld_normal; + +__END_DECLS #endif diff --git a/launchd/src/launch_priv.h b/launchd/src/launch_priv.h index 3fc9082..3b016d3 100644 --- a/launchd/src/launch_priv.h +++ b/launchd/src/launch_priv.h @@ -1,70 +1,118 @@ /* * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ + * @APPLE_APACHE_LICENSE_HEADER_START@ * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * The 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, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and * limitations under the License. * - * @APPLE_LICENSE_HEADER_END@ + * @APPLE_APACHE_LICENSE_HEADER_END@ */ #ifndef _LAUNCH_PRIV_H_ #define _LAUNCH_PRIV_H_ -#define LAUNCH_KEY_GETUSERENVIRONMENT "GetUserEnvironment" -#define LAUNCH_KEY_SETUSERENVIRONMENT "SetUserEnvironment" -#define LAUNCH_KEY_UNSETUSERENVIRONMENT "UnsetUserEnvironment" -#define LAUNCH_KEY_SETSTDOUT "SetStandardOut" -#define LAUNCH_KEY_SETSTDERR "SetStandardError" -#define LAUNCH_KEY_SHUTDOWN "Shutdown" -#define LAUNCH_KEY_GETRESOURCELIMITS "GetResourceLimits" -#define LAUNCH_KEY_SETRESOURCELIMITS "SetResourceLimits" -#define LAUNCH_KEY_RELOADTTYS "ReloadTTYS" -#define LAUNCH_KEY_SETLOGMASK "SetLogMask" -#define LAUNCH_KEY_GETLOGMASK "GetLogMask" -#define LAUNCH_KEY_SETUMASK "SetUmask" -#define LAUNCH_KEY_GETUMASK "GetUmask" -#define LAUNCH_KEY_GETRUSAGESELF "GetResourceUsageSelf" -#define LAUNCH_KEY_GETRUSAGECHILDREN "GetResourceUsageChildren" - -#define LAUNCH_KEY_WORKAROUNDBONJOUR "WorkaroundBonjour" +#include + +__BEGIN_DECLS + +#define LAUNCH_KEY_GETUSERENVIRONMENT "GetUserEnvironment" +#define LAUNCH_KEY_SETUSERENVIRONMENT "SetUserEnvironment" +#define LAUNCH_KEY_UNSETUSERENVIRONMENT "UnsetUserEnvironment" +#define LAUNCH_KEY_SETSTDOUT "SetStandardOut" +#define LAUNCH_KEY_SETSTDERR "SetStandardError" +#define LAUNCH_KEY_SHUTDOWN "Shutdown" +#define LAUNCH_KEY_SINGLEUSER "SingleUser" +#define LAUNCH_KEY_GETRESOURCELIMITS "GetResourceLimits" +#define LAUNCH_KEY_SETRESOURCELIMITS "SetResourceLimits" +#define LAUNCH_KEY_RELOADTTYS "ReloadTTYS" +#define LAUNCH_KEY_SETLOGMASK "SetLogMask" +#define LAUNCH_KEY_GETLOGMASK "GetLogMask" +#define LAUNCH_KEY_SETUMASK "SetUmask" +#define LAUNCH_KEY_GETUMASK "GetUmask" +#define LAUNCH_KEY_GETRUSAGESELF "GetResourceUsageSelf" +#define LAUNCH_KEY_GETRUSAGECHILDREN "GetResourceUsageChildren" #define LAUNCHD_SOCKET_ENV "LAUNCHD_SOCKET" -#define LAUNCHD_SOCK_PREFIX "/var/launchd" +#define LAUNCHD_SOCK_PREFIX "/var/tmp/launchd" #define LAUNCHD_TRUSTED_FD_ENV "__LAUNCHD_FD" #define LAUNCHD_ASYNC_MSG_KEY "_AsyncMessage" #define LAUNCH_KEY_BATCHCONTROL "BatchControl" #define LAUNCH_KEY_BATCHQUERY "BatchQuery" -typedef struct _launch *launch_t; +#define LAUNCH_JOBKEY_MACH_KUNCSERVER "kUNCServer" +#define LAUNCH_JOBKEY_MACH_EXCEPTIONSERVER "ExceptionServer" +#define LAUNCH_JOBKEY_MACH_TASKSPECIALPORT "TaskSpecialPort" +#define LAUNCH_JOBKEY_MACH_HOSTSPECIALPORT "HostSpecialPort" -extern void (*__log_liblaunch_bug)(const char *path, unsigned int line, const char *test); +typedef struct _launch *launch_t; launch_t launchd_fdopen(int); int launchd_getfd(launch_t); void launchd_close(launch_t); launch_data_t launch_data_new_errno(int); -bool launch_data_set_errno(launch_data_t, int); +bool launch_data_set_errno(launch_data_t, int); int launchd_msg_send(launch_t, launch_data_t); int launchd_msg_recv(launch_t, void (*)(launch_data_t, void *), void *); +/* For LoginWindow. + * + * After this call, the task's bootstrap port is set to the per session launchd. + * + * This returns the PID on of the per session launchd, and -1 on failure. + * + * If launchd terminates, loginwindow should exit. + * If loginwindow terminates, launchd will exit. + */ +#define LOAD_ONLY_SAFEMODE_LAUNCHAGENTS 1 +pid_t create_and_switch_to_per_session_launchd(const char *login, int flags, ...); + /* batch jobs will be implicity re-enabled when the last application who - * disabled them exits */ + * disabled them exits. + * + * This API is really a hack to work around the lack of real-time APIs + * at the VFS layer. + */ void launchd_batch_enable(bool); bool launchd_batch_query(void); +/* For CoreProcesses + */ + +#define SPAWN_VIA_LAUNCHD_STOPPED 0x0001 +#define SPAWN_VIA_LAUNCHD_FORCE_PPC 0x0002 + +struct spawn_via_launchd_attr { + uint64_t spawn_flags; + const char * spawn_path; + const char * spawn_chdir; + const char *const * spawn_env; + const mode_t * spawn_umask; + mach_port_t * spawn_observer_port; +}; + +#define spawn_via_launchd(a, b, c) _spawn_via_launchd(a, b, c, 0) +pid_t _spawn_via_launchd( + const char *label, + const char *const *argv, + const struct spawn_via_launchd_attr *spawn_attrs, + int struct_version); + +kern_return_t mpm_wait(mach_port_t ajob, int *wstatus); + +kern_return_t mpm_uncork_fork(mach_port_t ajob); + + +__END_DECLS + #endif diff --git a/launchd/src/launchctl.1 b/launchd/src/launchctl.1 index 8a45e85..4645525 100644 --- a/launchd/src/launchctl.1 +++ b/launchd/src/launchctl.1 @@ -25,27 +25,70 @@ starts. .Sh SUBCOMMANDS .Bl -tag -width -indent .It Xo Ar load Op Fl wF +.Op Fl S Ar sessiontype +.Op Fl D Ar domain .Ar paths ... .Xc Load the specified configuration files or directories of configuration files. +Jobs that are not on-demand will be started as soon as possible. +All specified jobs will be loaded before any of them are allowed to start. .Bl -tag -width -indent .It Fl w Remove the disabled key and write the configuration files back out to disk. .It Fl F Force the loading of the plist. Ignore the Disabled key. +.It Fl S Ar sessiontype +Some jobs only make sense in certain contexts. This flag instructs +.Nm launchctl +to look for jobs in a different location when using the -D flag, and allows +.Nm launchctl +to restrict which jobs are loaded into which session types. Currently known +session types include: Aqua, X11 and tty. +.It Fl D Ar domain +Look for +.Xr plist 5 files ending in *.plist in the domain given. Valid domains include +"system," "local," "network" and "all." When providing a session type, an additional +domain is available for use called "user." For example, without a session type given, +"-D system" would load from property list files from /System/Library/LaunchDaemons. +With a session type passed, it would load from /System/Library/LaunchAgents. .El .It Xo Ar unload Op Fl w .Ar paths ... .Xc Unload the specified configuration files or directories of configuration files. +This will also stop the job if it is running. .Bl -tag -width -indent .It Fl w Add the disabled key and write the configuration files back out to disk. .El -.It Ar start Ar joblabels ... -Start the specified jobs by label. -.It Ar stop Ar joblabels ... -Stop the specified jobs by label. Jobs may restart automatically if demand driven. +.It Xo Ar submit Fl l Ar label +.Op Fl p Ar executable +.Op Fl o Ar path +.Op Fl e Ar path +.Ar -- command +.Op Ar args +.Xc +A simple way of submitting a program to run without a configuration file. This mechanism also tells launchd to keep the program alive in the event of failure. +.Bl -tag -width -indent +.It Fl l Ar label +What unique label to assign this job to launchd. +.It Fl p Ar program +What program to really execute, regardless of what follows the -- in the submit sub-command. +.It Fl o Ar path +Where to send the stdout of the program. +.It Fl e Ar path +Where to send the stderr of the program. +.El +.It Ar remove Ar job_label +Remove the jobs from launchd by label. +.It Ar start Ar job_label +Start the specified jobs by label. The expected use of this subcommand is for +debugging and testing so that one can manually kick-start an on-demand server. +.It Ar stop Ar job_label +Stop the specified jobs by label. If a job is on-demand, launchd may immediately +restart the job if launchd finds any criteria that is satisfied. +Non-demand based jobs will always be restarted. Use of this subcommand is discouraged. +Jobs should ideally idle timeout by themselves. .It Ar list List all of the jobs loaded into .Nm launchd . @@ -106,6 +149,14 @@ Get or optionally set the .Xr umask 2 of .Nm launchd . +.It Ar bslist Op Ar PID +This prints out Mach bootstrap services and their respective states. While the +namespace appears flat, it is in fact hierarchical, thus allowing for certain +services to be only available to a subset of processes. The three states a +service can be in are active ("A"), in-active ("I") and on-demand ("D"). +.It Ar bsexec Ar PID command Op Ar args +This executes the given command in the same Mach bootstrap namespace hierachy +as the given PID. .It Ar help Print out a quick usage statement. .El diff --git a/launchd/src/launchctl.c b/launchd/src/launchctl.c index 8b38d26..a3b78d2 100644 --- a/launchd/src/launchctl.c +++ b/launchd/src/launchctl.c @@ -1,30 +1,33 @@ /* * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ + * @APPLE_APACHE_LICENSE_HEADER_START@ * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * The 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, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and * limitations under the License. * - * @APPLE_LICENSE_HEADER_END@ + * @APPLE_APACHE_LICENSE_HEADER_END@ */ + +static const char *const __rcs_file_version__ = "$Revision: 1.88 $"; + #include +#include +#include #include -#include #include +#include #include +#include #include #include #include @@ -32,7 +35,12 @@ #include #include #include +#include +#include +#include #include +#include +#include #include #include #include @@ -43,10 +51,16 @@ #include #include #include +#include #include #include #include +#include +#include +#include +#include "bootstrap_public.h" +#include "bootstrap_private.h" #include "launch.h" #include "launch_priv.h" @@ -55,9 +69,21 @@ #define MACHINIT_JOBKEY_ONDEMAND "OnDemand" #define MACHINIT_JOBKEY_SERVICENAME "ServiceName" #define MACHINIT_JOBKEY_COMMAND "Command" -#define MACHINIT_JOBKEY_ISKUNCSERVER "isKUNCServer" +#define MACHINIT_JOBKEY_SERVERPORT "ServerPort" +#define MACHINIT_JOBKEY_SERVICEPORT "ServicePort" + +#define assumes(e) \ + (__builtin_expect(!(e), 0) ? _log_launchctl_bug(__rcs_file_version__, __FILE__, __LINE__, #e), false : true) +struct load_unload_state { + launch_data_t pass0; + launch_data_t pass1; + launch_data_t pass2; + char *session_type; + unsigned int editondisk:1, load:1, forceload:1, __pad:29; +}; + static void myCFDictionaryApplyFunction(const void *key, const void *value, void *context); static bool launch_data_array_append(launch_data_t a, launch_data_t o); static void distill_jobs(launch_data_t); @@ -68,22 +94,49 @@ static launch_data_t CF2launch_data(CFTypeRef); static launch_data_t read_plist_file(const char *file, bool editondisk, bool load); static CFPropertyListRef CreateMyPropertyListFromFile(const char *); static void WriteMyPropertyListToFile(CFPropertyListRef, const char *); -static void readpath(const char *, launch_data_t, launch_data_t, bool editondisk, bool load, bool forceload); -static void readfile(const char *, launch_data_t, launch_data_t, bool editondisk, bool load, bool forceload); +static bool path_goodness_check(const char *path, bool forceload); +static void readpath(const char *, struct load_unload_state *); +static void readfile(const char *, struct load_unload_state *); static int _fd(int); static int demux_cmd(int argc, char *const argv[]); -static void do_rendezvous_magic(const struct addrinfo *res, const char *serv, const char *label); -static void workaround_bonjour_asynchronously(void); +static launch_data_t do_rendezvous_magic(const struct addrinfo *res, const char *serv); static void submit_job_pass(launch_data_t jobs); static void submit_mach_jobs(launch_data_t jobs); -static void let_go_of_mach_jobs(void); +static void let_go_of_mach_jobs(launch_data_t jobs); static void do_mgroup_join(int fd, int family, int socktype, int protocol, const char *mgroup); -static void print_jobs(launch_data_t j, const char *label, void *context); +static mach_port_t str2bsport(const char *s); +static void print_jobs(launch_data_t j); +static void print_obj(launch_data_t obj, const char *key, void *context); static bool is_legacy_mach_job(launch_data_t obj); - +static bool delay_to_second_pass(launch_data_t o); +static void delay_to_second_pass2(launch_data_t o, const char *key, void *context); +static bool str2lim(const char *buf, rlim_t *res); +static const char *lim2str(rlim_t val, char *buf); +static const char *num2name(int n); +static ssize_t name2num(const char *n); +static void unloadjob(launch_data_t job); +static void print_key_value(launch_data_t obj, const char *key, void *context); +static void print_launchd_env(launch_data_t obj, const char *key, void *context); +static void _log_launchctl_bug(const char *rcs_rev, const char *path, unsigned int line, const char *test); +static void loopback_setup_ipv4(void); +static void loopback_setup_ipv6(void); +static pid_t fwexec(const char *const *argv, bool _wait); +static void do_potential_fsck(void); +static bool path_check(const char *path); +static bool is_safeboot(void); +static bool is_netboot(void); +static void apply_func_to_dir(const char *thedir, void (*thefunc)(const char *)); +static void apply_sysctls_from_file(const char *thefile); +static void empty_dir(const char *path); +static int touch_file(const char *path, mode_t m); +static void do_sysversion_sysctl(void); +static void workaround4465949(void); + +static int bootstrap_cmd(int argc, char *const argv[]); static int load_and_unload_cmd(int argc, char *const argv[]); //static int reload_cmd(int argc, char *const argv[]); -static int start_and_stop_cmd(int argc, char *const argv[]); +static int start_stop_remove_cmd(int argc, char *const argv[]); +static int submit_cmd(int argc, char *const argv[]); static int list_cmd(int argc, char *const argv[]); static int setenv_cmd(int argc, char *const argv[]); @@ -96,7 +149,10 @@ static int fyi_cmd(int argc, char *const argv[]); static int logupdate_cmd(int argc, char *const argv[]); static int umask_cmd(int argc, char *const argv[]); static int getrusage_cmd(int argc, char *const argv[]); +static int bsexec_cmd(int argc, char *const argv[]); +static int bslist_cmd(int argc, char *const argv[]); +static int exit_cmd(int argc, char *const argv[]) __attribute__((noreturn)); static int help_cmd(int argc, char *const argv[]); static const struct { @@ -107,8 +163,11 @@ static const struct { { "load", load_and_unload_cmd, "Load configuration files and/or directories" }, { "unload", load_and_unload_cmd, "Unload configuration files and/or directories" }, // { "reload", reload_cmd, "Reload configuration files and/or directories" }, - { "start", start_and_stop_cmd, "Start specified jobs" }, - { "stop", start_and_stop_cmd, "Stop specified jobs" }, + { "start", start_stop_remove_cmd, "Start specified job" }, + { "stop", start_stop_remove_cmd, "Stop specified job" }, + { "submit", submit_cmd, "Submit a job from the command line" }, + { "remove", start_stop_remove_cmd, "Remove specified job" }, + { "bootstrap", bootstrap_cmd, "Bootstrap launchd" }, { "list", list_cmd, "List jobs and information about jobs" }, { "setenv", setenv_cmd, "Set an environmental variable in launchd" }, { "unsetenv", unsetenv_cmd, "Unset an environmental variable in launchd" }, @@ -118,50 +177,85 @@ static const struct { { "stdout", stdio_cmd, "Redirect launchd's standard out to the given path" }, { "stderr", stdio_cmd, "Redirect launchd's standard error to the given path" }, { "shutdown", fyi_cmd, "Prepare for system shutdown" }, + { "singleuser", fyi_cmd, "Switch to single-user mode" }, { "reloadttys", fyi_cmd, "Reload /etc/ttys" }, { "getrusage", getrusage_cmd, "Get resource usage statistics from launchd" }, { "log", logupdate_cmd, "Adjust the logging level or mask of launchd" }, { "umask", umask_cmd, "Change launchd's umask" }, + { "bsexec", bsexec_cmd, "Execute a process within a different Mach bootstrap subset" }, + { "bslist", bslist_cmd, "List Mach bootstrap services and optional servers" }, + { "exit", exit_cmd, "Exit the interactive invocation of launchctl" }, + { "quit", exit_cmd, "Quit the interactive invocation of launchctl" }, { "help", help_cmd, "This help output" }, }; -int main(int argc, char *const argv[]) +static bool istty = false; +static bool verbose = false; + +int +main(int argc, char *const argv[]) { - bool istty = isatty(STDIN_FILENO); char *l; - if (argc > 1) - exit(demux_cmd(argc - 1, argv + 1)); + do_sysversion_sysctl(); + + istty = isatty(STDIN_FILENO); + + argc--, argv++; + + if (argc > 0 && argv[0][0] == '-') { + char *flago; + + for (flago = argv[0] + 1; *flago; flago++) { + switch (*flago) { + case 'v': + verbose = true; + break; + default: + fprintf(stderr, "Unknown argument: '-%c'\n", *flago); + break; + } + } + argc--, argv++; + } if (NULL == readline) { fprintf(stderr, "missing library: readline\n"); exit(EXIT_FAILURE); } - while ((l = readline(istty ? "launchd% " : NULL))) { - char *inputstring = l, *argv2[100], **ap = argv2; - int i = 0; - - while ((*ap = strsep(&inputstring, " \t"))) { - if (**ap != '\0') { - ap++; - i++; + if (argc == 0) { + while ((l = readline(istty ? "launchd% " : NULL))) { + char *inputstring = l, *argv2[100], **ap = argv2; + int i = 0; + + while ((*ap = strsep(&inputstring, " \t"))) { + if (**ap != '\0') { + ap++; + i++; + } } - } - if (i > 0) - demux_cmd(i, argv2); + if (i > 0) + demux_cmd(i, argv2); + + free(l); + } - free(l); + if (istty) { + fputc('\n', stdout); + } } - if (istty) - fputc('\n', stdout); + if (argc > 0) { + exit(demux_cmd(argc, argv)); + } exit(EXIT_SUCCESS); } -static int demux_cmd(int argc, char *const argv[]) +int +demux_cmd(int argc, char *const argv[]) { size_t i; @@ -177,7 +271,8 @@ static int demux_cmd(int argc, char *const argv[]) return 1; } -static int unsetenv_cmd(int argc, char *const argv[]) +int +unsetenv_cmd(int argc, char *const argv[]) { launch_data_t resp, tmp, msg; @@ -204,7 +299,8 @@ static int unsetenv_cmd(int argc, char *const argv[]) return 0; } -static int setenv_cmd(int argc, char *const argv[]) +int +setenv_cmd(int argc, char *const argv[]) { launch_data_t resp, tmp, tmpv, msg; @@ -232,17 +328,20 @@ static int setenv_cmd(int argc, char *const argv[]) return 0; } -static void print_launchd_env(launch_data_t obj, const char *key, void *context) +void +print_launchd_env(launch_data_t obj, const char *key, void *context) { bool *is_csh = context; + /* XXX escape the double quotes */ if (*is_csh) - fprintf(stdout, "setenv %s %s;\n", key, launch_data_get_string(obj)); + fprintf(stdout, "setenv %s \"%s\";\n", key, launch_data_get_string(obj)); else - fprintf(stdout, "%s=%s; export %s;\n", key, launch_data_get_string(obj), key); + fprintf(stdout, "%s=\"%s\"; export %s;\n", key, launch_data_get_string(obj), key); } -static void print_key_value(launch_data_t obj, const char *key, void *context) +void +print_key_value(launch_data_t obj, const char *key, void *context) { const char *k = context; @@ -250,7 +349,8 @@ static void print_key_value(launch_data_t obj, const char *key, void *context) fprintf(stdout, "%s\n", launch_data_get_string(obj)); } -static int getenv_and_export_cmd(int argc, char *const argv[] __attribute__((unused))) +int +getenv_and_export_cmd(int argc, char *const argv[] __attribute__((unused))) { launch_data_t resp, msg; bool is_csh = false; @@ -284,7 +384,8 @@ static int getenv_and_export_cmd(int argc, char *const argv[] __attribute__((unu return 0; } -static void unloadjob(launch_data_t job) +void +unloadjob(launch_data_t job) { launch_data_t resp, tmp, tmps, msg; int e; @@ -340,55 +441,196 @@ read_plist_file(const char *file, bool editondisk, bool load) } void -readfile(const char *what, launch_data_t pass0, launch_data_t pass1, bool editondisk, bool load, bool forceload) +delay_to_second_pass2(launch_data_t o, const char *key, void *context) +{ + bool *res = context; + size_t i; + + if (key && 0 == strcmp(key, LAUNCH_JOBSOCKETKEY_BONJOUR)) { + *res = true; + return; + } + + switch (launch_data_get_type(o)) { + case LAUNCH_DATA_DICTIONARY: + launch_data_dict_iterate(o, delay_to_second_pass2, context); + break; + case LAUNCH_DATA_ARRAY: + for (i = 0; i < launch_data_array_get_count(o); i++) + delay_to_second_pass2(launch_data_array_get_index(o, i), NULL, context); + break; + default: + break; + } +} + +bool +delay_to_second_pass(launch_data_t o) +{ + bool res = false; + + launch_data_t socks = launch_data_dict_lookup(o, LAUNCH_JOBKEY_SOCKETS); + + if (NULL == socks) + return false; + + delay_to_second_pass2(socks, NULL, &res); + + return res; +} + +void +readfile(const char *what, struct load_unload_state *lus) { - launch_data_t tmpd, thejob; + char ourhostname[1024]; + launch_data_t tmpd, tmps, thejob, tmpa; bool job_disabled = false; + size_t i, c; + + gethostname(ourhostname, sizeof(ourhostname)); - if (NULL == (thejob = read_plist_file(what, editondisk, load))) { + if (NULL == (thejob = read_plist_file(what, lus->editondisk, lus->load))) { fprintf(stderr, "%s: no plist was returned for: %s\n", getprogname(), what); return; } if (is_legacy_mach_job(thejob)) { - launch_data_array_append(pass0, thejob); + fprintf(stderr, "%s: Please convert the following to launchd: %s\n", getprogname(), what); + launch_data_array_append(lus->pass0, thejob); return; } if (NULL == launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_LABEL)) { fprintf(stderr, "%s: missing the Label key: %s\n", getprogname(), what); - launch_data_free(thejob); - return; + goto out_bad; + } + + if (NULL != (tmpa = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_LIMITLOADFROMHOSTS))) { + c = launch_data_array_get_count(tmpa); + + for (i = 0; i < c; i++) { + launch_data_t oai = launch_data_array_get_index(tmpa, i); + if (!strcasecmp(ourhostname, launch_data_get_string(oai))) + goto out_bad; + } + } + + if (NULL != (tmpa = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_LIMITLOADTOHOSTS))) { + c = launch_data_array_get_count(tmpa); + + for (i = 0; i < c; i++) { + launch_data_t oai = launch_data_array_get_index(tmpa, i); + if (!strcasecmp(ourhostname, launch_data_get_string(oai))) + break; + } + + if (i == c) + goto out_bad; + } + + if ((tmpa = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE))) { + const char *allowed_session; + bool skipjob = true; + + if (lus->session_type) switch (launch_data_get_type(tmpa)) { + case LAUNCH_DATA_ARRAY: + c = launch_data_array_get_count(tmpa); + for (i = 0; i < c; i++) { + tmps = launch_data_array_get_index(tmpa, i); + allowed_session = launch_data_get_string(tmps); + if (strcasecmp(lus->session_type, allowed_session) == 0) { + skipjob = false; + break; + } + } + break; + case LAUNCH_DATA_STRING: + allowed_session = launch_data_get_string(tmpa); + if (strcasecmp(lus->session_type, allowed_session) == 0) { + skipjob = false; + } + break; + default: + break; + } + + if (skipjob) { + goto out_bad; + } } if ((tmpd = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_DISABLED))) job_disabled = launch_data_get_bool(tmpd); - if (forceload) + if (lus->forceload) job_disabled = false; - if (job_disabled && load) { - launch_data_free(thejob); - return; + if (job_disabled && lus->load) + goto out_bad; + + if (delay_to_second_pass(thejob)) + launch_data_array_append(lus->pass2, thejob); + else + launch_data_array_append(lus->pass1, thejob); + + if (verbose) + fprintf(stdout, "Will load: %s\n", what); + + return; +out_bad: + if (verbose) + fprintf(stdout, "Ignored: %s\n", what); + launch_data_free(thejob); +} + +bool +path_goodness_check(const char *path, bool forceload) +{ + struct stat sb; + + if (stat(path, &sb) == -1) { + fprintf(stderr, "%s: Couldn't stat(\"%s\"): %s\n", getprogname(), path, strerror(errno)); + return false; } - launch_data_array_append(pass1, thejob); + if (forceload) + return true; + + if (sb.st_mode & (S_IWOTH|S_IWGRP)) { + fprintf(stderr, "%s: Dubious permissions on file (skipping): %s\n", getprogname(), path); + return false; + } + + if (sb.st_uid != 0 && sb.st_uid != getuid()) { + fprintf(stderr, "%s: Dubious ownership on file (skipping): %s\n", getprogname(), path); + return false; + } + + if (!(S_ISREG(sb.st_mode) || S_ISDIR(sb.st_mode))) { + fprintf(stderr, "%s: Dubious path. Not a regular file or directory (skipping): %s\n", getprogname(), path); + return false; + } + + return true; } void -readpath(const char *what, launch_data_t pass0, launch_data_t pass1, bool editondisk, bool load, bool forceload) +readpath(const char *what, struct load_unload_state *lus) { char buf[MAXPATHLEN]; struct stat sb; struct dirent *de; DIR *d; + if (!path_goodness_check(what, lus->forceload)) + return; + if (stat(what, &sb) == -1) return; - if (S_ISREG(sb.st_mode) && !(sb.st_mode & S_IWOTH)) { - readfile(what, pass0, pass1, editondisk, load, forceload); - } else { + if (S_ISREG(sb.st_mode)) { + readfile(what, lus); + } else if (S_ISDIR(sb.st_mode)) { if ((d = opendir(what)) == NULL) { fprintf(stderr, "%s: opendir() failed to open the directory\n", getprogname()); return; @@ -399,7 +641,10 @@ readpath(const char *what, launch_data_t pass0, launch_data_t pass1, bool editon continue; snprintf(buf, sizeof(buf), "%s/%s", what, de->d_name); - readfile(buf, pass0, pass1, editondisk, load, forceload); + if (!path_goodness_check(buf, lus->forceload)) + continue; + + readfile(buf, lus); } closedir(d); } @@ -425,14 +670,15 @@ distill_config_file(launch_data_t id_plist) struct distill_context dc = { id_plist, NULL }; launch_data_t tmp; - if ((tmp = launch_data_dict_lookup(id_plist, LAUNCH_JOBKEY_SOCKETS))) { + if ((tmp = launch_data_dict_lookup(dc.base, LAUNCH_JOBKEY_SOCKETS))) { dc.newsockdict = launch_data_alloc(LAUNCH_DATA_DICTIONARY); launch_data_dict_iterate(tmp, sock_dict_cb, &dc); launch_data_dict_insert(dc.base, dc.newsockdict, LAUNCH_JOBKEY_SOCKETS); } } -static void sock_dict_cb(launch_data_t what, const char *key, void *context) +void +sock_dict_cb(launch_data_t what, const char *key, void *context) { struct distill_context *dc = context; launch_data_t fdarray = launch_data_alloc(LAUNCH_DATA_ARRAY); @@ -452,16 +698,13 @@ static void sock_dict_cb(launch_data_t what, const char *key, void *context) } } -static void sock_dict_edit_entry(launch_data_t tmp, const char *key, launch_data_t fdarray, launch_data_t thejob) +void +sock_dict_edit_entry(launch_data_t tmp, const char *key, launch_data_t fdarray, launch_data_t thejob) { launch_data_t a, val; - const char *joblabel; int sfd, st = SOCK_STREAM; bool passive = true; - assert((val = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_LABEL)) != NULL); - joblabel = launch_data_get_string(val); - if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_TYPE))) { if (!strcasecmp(launch_data_get_string(val), "stream")) { st = SOCK_STREAM; @@ -591,6 +834,7 @@ static void sock_dict_edit_entry(launch_data_t tmp, const char *key, launch_data } for (res = res0; res; res = res->ai_next) { + launch_data_t rvs_fd = NULL; if ((sfd = _fd(socket(res->ai_family, res->ai_socktype, res->ai_protocol))) == -1) { fprintf(stderr, "socket(): %s\n", strerror(errno)); return; @@ -627,22 +871,30 @@ static void sock_dict_edit_entry(launch_data_t tmp, const char *key, launch_data } if (rendezvous && (res->ai_family == AF_INET || res->ai_family == AF_INET6) && (res->ai_socktype == SOCK_STREAM || res->ai_socktype == SOCK_DGRAM)) { + launch_data_t rvs_fds = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_BONJOURFDS); + if (NULL == rvs_fds) { + rvs_fds = launch_data_alloc(LAUNCH_DATA_ARRAY); + launch_data_dict_insert(thejob, rvs_fds, LAUNCH_JOBKEY_BONJOURFDS); + } if (NULL == rnames) { - do_rendezvous_magic(res, serv, joblabel); + rvs_fd = do_rendezvous_magic(res, serv); + if (rvs_fd) + launch_data_array_append(rvs_fds, rvs_fd); } else if (LAUNCH_DATA_STRING == launch_data_get_type(rnames)) { - do_rendezvous_magic(res, launch_data_get_string(rnames), joblabel); + rvs_fd = do_rendezvous_magic(res, launch_data_get_string(rnames)); + if (rvs_fd) + launch_data_array_append(rvs_fds, rvs_fd); } else if (LAUNCH_DATA_ARRAY == launch_data_get_type(rnames)) { size_t rn_i, rn_ac = launch_data_array_get_count(rnames); for (rn_i = 0; rn_i < rn_ac; rn_i++) { launch_data_t rn_tmp = launch_data_array_get_index(rnames, rn_i); - do_rendezvous_magic(res, launch_data_get_string(rn_tmp), joblabel); + rvs_fd = do_rendezvous_magic(res, launch_data_get_string(rn_tmp)); + if (rvs_fd) + launch_data_array_append(rvs_fds, rvs_fd); } } - /* Launchd should not register the same service more than once */ - /* Switch to DNSServiceRegisterAddrInfo() */ - rendezvous = false; } } else { if (connect(sfd, res->ai_addr, res->ai_addrlen) == -1) { @@ -651,12 +903,18 @@ static void sock_dict_edit_entry(launch_data_t tmp, const char *key, launch_data } } val = launch_data_new_fd(sfd); + if (rvs_fd) { + /* Launchd should not register the same service more than once */ + /* Switch to DNSServiceRegisterAddrInfo() */ + rendezvous = false; + } launch_data_array_append(fdarray, val); } } } -static void do_mgroup_join(int fd, int family, int socktype, int protocol, const char *mgroup) +void +do_mgroup_join(int fd, int family, int socktype, int protocol, const char *mgroup) { struct addrinfo hints, *res0, *res; struct ip_mreq mreq; @@ -701,95 +959,41 @@ static void do_mgroup_join(int fd, int family, int socktype, int protocol, const freeaddrinfo(res0); } -struct bonjour_magic { - SLIST_ENTRY(bonjour_magic) sle; - char *str; - int port; - char label[0]; -}; - -static SLIST_HEAD(, bonjour_magic) bm_later = { NULL }; - -void -do_rendezvous_magic(const struct addrinfo *res, const char *serv, const char *joblabel) -{ - struct bonjour_magic *bm = calloc(1, sizeof(struct bonjour_magic) + strlen(joblabel) + 1); - const char *typestr = "udp"; - - if (res->ai_socktype == SOCK_STREAM) - typestr = "tcp"; - - strcpy(bm->label, joblabel); - - asprintf(&bm->str, "_%s._%s.", serv, typestr); - - if (res->ai_family == AF_INET) { - bm->port = ((struct sockaddr_in *)res->ai_addr)->sin_port; - } else { - bm->port = ((struct sockaddr_in6 *)res->ai_addr)->sin6_port; - } - - SLIST_INSERT_HEAD(&bm_later, bm, sle); -} - -void -workaround_bonjour_asynchronously(void) +launch_data_t +do_rendezvous_magic(const struct addrinfo *res, const char *serv) { - launch_data_t resp, msg, msgpayload, tmpa; - struct bonjour_magic *bm; - DNSServiceErrorType error; + struct stat sb; DNSServiceRef service; - int fd; - - if (SLIST_EMPTY(&bm_later)) - return; - - if (fork() != 0) - return; - - signal(SIGHUP, SIG_IGN); - setsid(); - sleep(30); + DNSServiceErrorType error; + char rvs_buf[200]; + short port; + static int statres = 1; - msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY); - msgpayload = launch_data_alloc(LAUNCH_DATA_DICTIONARY); + if (1 == statres) + statres = stat("/usr/sbin/mDNSResponder", &sb); - SLIST_FOREACH(bm, &bm_later, sle) { - service = NULL; - error = DNSServiceRegister(&service, 0, 0, NULL, bm->str, NULL, NULL, bm->port, 0, NULL, NULL, NULL); - if (error != kDNSServiceErr_NoError) { - fprintf(stderr, "DNSServiceRegister(\"%s\"): %d\n", bm->str, error); - continue; - } - fd = DNSServiceRefSockFD(service); - tmpa = launch_data_dict_lookup(msgpayload, bm->label); - if (!tmpa) { - tmpa = launch_data_alloc(LAUNCH_DATA_ARRAY); - launch_data_dict_insert(msgpayload, tmpa, bm->label); - } - launch_data_array_append(tmpa, launch_data_new_fd(fd)); - } + if (-1 == statres) + return NULL; - launch_data_dict_insert(msg, msgpayload, LAUNCH_KEY_WORKAROUNDBONJOUR); + sprintf(rvs_buf, "_%s._%s.", serv, res->ai_socktype == SOCK_STREAM ? "tcp" : "udp"); - resp = launch_msg(msg); + if (res->ai_family == AF_INET) + port = ((struct sockaddr_in *)res->ai_addr)->sin_port; + else + port = ((struct sockaddr_in6 *)res->ai_addr)->sin6_port; - launch_data_free(msg); + error = DNSServiceRegister(&service, 0, 0, NULL, rvs_buf, NULL, NULL, port, 0, NULL, NULL, NULL); - if (resp) { - if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) { - if ((errno = launch_data_get_errno(resp))) { - fprintf(stderr, "Workaround Bonjour: %s\n", strerror(errno)); - } - } - launch_data_free(resp); - } + if (error == kDNSServiceErr_NoError) + return launch_data_new_fd(DNSServiceRefSockFD(service)); - _exit(EXIT_SUCCESS); + fprintf(stderr, "DNSServiceRegister(\"%s\"): %d\n", serv, error); + return NULL; } -static CFPropertyListRef CreateMyPropertyListFromFile(const char *posixfile) +CFPropertyListRef +CreateMyPropertyListFromFile(const char *posixfile) { CFPropertyListRef propertyList; CFStringRef errorString; @@ -809,7 +1013,8 @@ static CFPropertyListRef CreateMyPropertyListFromFile(const char *posixfile) return propertyList; } -static void WriteMyPropertyListToFile(CFPropertyListRef plist, const char *posixfile) +void +WriteMyPropertyListToFile(CFPropertyListRef plist, const char *posixfile) { CFDataRef resourceData; CFURLRef fileURL; @@ -825,7 +1030,8 @@ static void WriteMyPropertyListToFile(CFPropertyListRef plist, const char *posix fprintf(stderr, "%s: CFURLWriteDataAndPropertiesToResource(%s) failed: %d\n", getprogname(), posixfile, (int)errorCode); } -void myCFDictionaryApplyFunction(const void *key, const void *value, void *context) +void +myCFDictionaryApplyFunction(const void *key, const void *value, void *context) { launch_data_t ik, iw, where = context; @@ -836,7 +1042,8 @@ void myCFDictionaryApplyFunction(const void *key, const void *value, void *conte launch_data_free(ik); } -static launch_data_t CF2launch_data(CFTypeRef cfr) +launch_data_t +CF2launch_data(CFTypeRef cfr) { launch_data_t r; CFTypeID cft = CFGetTypeID(cfr); @@ -844,9 +1051,11 @@ static launch_data_t CF2launch_data(CFTypeRef cfr) if (cft == CFStringGetTypeID()) { char buf[4096]; CFStringGetCString(cfr, buf, sizeof(buf), kCFStringEncodingUTF8); - r = launch_data_new_string(buf); + r = launch_data_alloc(LAUNCH_DATA_STRING); + launch_data_set_string(r, buf); } else if (cft == CFBooleanGetTypeID()) { - r = launch_data_new_bool(CFBooleanGetValue(cfr)); + r = launch_data_alloc(LAUNCH_DATA_BOOL); + launch_data_set_bool(r, CFBooleanGetValue(cfr)); } else if (cft == CFArrayGetTypeID()) { CFIndex i, ac = CFArrayGetCount(cfr); r = launch_data_alloc(LAUNCH_DATA_ARRAY); @@ -861,7 +1070,8 @@ static launch_data_t CF2launch_data(CFTypeRef cfr) r = launch_data_alloc(LAUNCH_DATA_DICTIONARY); CFDictionaryApplyFunction(cfr, myCFDictionaryApplyFunction, r); } else if (cft == CFDataGetTypeID()) { - r = launch_data_new_opaque(CFDataGetBytePtr(cfr), CFDataGetLength(cfr)); + r = launch_data_alloc(LAUNCH_DATA_ARRAY); + launch_data_set_opaque(r, CFDataGetBytePtr(cfr), CFDataGetLength(cfr)); } else if (cft == CFNumberGetTypeID()) { long long n; double d; @@ -877,14 +1087,16 @@ static launch_data_t CF2launch_data(CFTypeRef cfr) case kCFNumberLongType: case kCFNumberLongLongType: CFNumberGetValue(cfr, kCFNumberLongLongType, &n); - r = launch_data_new_integer(n); + r = launch_data_alloc(LAUNCH_DATA_INTEGER); + launch_data_set_integer(r, n); break; case kCFNumberFloat32Type: case kCFNumberFloat64Type: case kCFNumberFloatType: case kCFNumberDoubleType: CFNumberGetValue(cfr, kCFNumberDoubleType, &d); - r = launch_data_new_real(d); + r = launch_data_alloc(LAUNCH_DATA_REAL); + launch_data_set_real(r, d); break; default: r = NULL; @@ -896,7 +1108,8 @@ static launch_data_t CF2launch_data(CFTypeRef cfr) return r; } -static int help_cmd(int argc, char *const argv[]) +int +help_cmd(int argc, char *const argv[]) { FILE *where = stdout; int l, cmdwidth = 0; @@ -906,53 +1119,242 @@ static int help_cmd(int argc, char *const argv[]) where = stderr; fprintf(where, "usage: %s \n", getprogname()); + for (i = 0; i < (sizeof cmds / sizeof cmds[0]); i++) { l = strlen(cmds[i].name); if (l > cmdwidth) cmdwidth = l; } + for (i = 0; i < (sizeof cmds / sizeof cmds[0]); i++) fprintf(where, "\t%-*s\t%s\n", cmdwidth, cmds[i].name, cmds[i].desc); return 0; } -static int _fd(int fd) +int +exit_cmd(int argc __attribute__((unused)), char *const argv[] __attribute__((unused))) +{ + exit(0); +} + +int +_fd(int fd) { if (fd >= 0) fcntl(fd, F_SETFD, 1); return fd; } -static int load_and_unload_cmd(int argc, char *const argv[]) +int +bootstrap_cmd(int argc __attribute__((unused)), char *const argv[] __attribute__((unused))) { - launch_data_t pass0, pass1; - int i, ch; - bool wflag = false; - bool lflag = false; - bool Fflag = false; + int memmib[] = { CTL_HW, HW_MEMSIZE }; + int mvnmib[] = { CTL_KERN, KERN_MAXVNODES }; + int hnmib[] = { CTL_KERN, KERN_HOSTNAME }; + uint64_t mem = 0; + uint32_t mvn; + size_t memsz = sizeof(mem); + struct group *tfp_gr; + + if (assumes((tfp_gr = getgrnam("procview")) != NULL)) { + int tfp_r_mib[3] = { CTL_KERN, KERN_TFP, KERN_TFP_READ_GROUP }; + gid_t tfp_r_gid = tfp_gr->gr_gid; + assumes(sysctl(tfp_r_mib, 3, NULL, NULL, &tfp_r_gid, sizeof(tfp_r_gid)) != -1); + } + + if (assumes((tfp_gr = getgrnam("procmod")) != NULL)) { + int tfp_rw_mib[3] = { CTL_KERN, KERN_TFP, KERN_TFP_RW_GROUP }; + gid_t tfp_rw_gid = tfp_gr->gr_gid; + assumes(sysctl(tfp_rw_mib, 3, NULL, NULL, &tfp_rw_gid, sizeof(tfp_rw_gid)) != -1); + } + + if (assumes(sysctl(memmib, 2, &mem, &memsz, NULL, 0) != -1)) { + mvn = mem / (64 * 1024) + 1024; + assumes(sysctl(mvnmib, 2, NULL, NULL, &mvn, sizeof(mvn)) != -1); + } + assumes(sysctl(hnmib, 2, NULL, NULL, "localhost", sizeof("localhost")) != -1); + + loopback_setup_ipv4(); + loopback_setup_ipv6(); + + apply_sysctls_from_file("/etc/sysctl-macosxserver.conf"); + apply_sysctls_from_file("/etc/sysctl.conf"); + + if (path_check("/System/Installation") && path_check("/etc/rc.cdrom")) { + const char *rccdrom_tool[] = { _PATH_BSHELL, "/etc/rc.cdrom", "multiuser", NULL }; + assumes(fwexec(rccdrom_tool, true) != -1); + assumes(reboot(RB_HALT) != -1); + _exit(EXIT_FAILURE); + } else if (is_netboot()) { + const char *rcnetboot_tool[] = { _PATH_BSHELL, "/etc/rc.netboot", "init", NULL }; + if (!assumes(fwexec(rcnetboot_tool, true) != -1)) { + assumes(reboot(RB_HALT) != -1); + _exit(EXIT_FAILURE); + } + } else { + do_potential_fsck(); + } + + if (path_check("/var/account/acct")) { + assumes(acct("/var/account/acct") != -1); + } + + if (path_check("/etc/fstab")) { + const char *mount_tool[] = { "mount", "-vat", "nonfs", NULL }; + assumes(fwexec(mount_tool, true) != -1); + } + + if (path_check("/etc/rc.installer_cleanup")) { + const char *rccleanup_tool[] = { _PATH_BSHELL, "/etc/rc.installer_cleanup", "multiuser", NULL }; + assumes(fwexec(rccleanup_tool, true) != -1); + } + + apply_func_to_dir(_PATH_VARRUN, empty_dir); + apply_func_to_dir(_PATH_TMP, empty_dir); + remove(_PATH_NOLOGIN); + + // XXX --> RMRF_ITEMS="/var/tmp/folders.* + + // 775 www:www /var/run/davlocks 4489695 + // 775 root:daemon /var/run/StartupItems + + assumes(touch_file(_PATH_UTMP, DEFFILEMODE) != -1); + assumes(touch_file(_PATH_UTMPX, DEFFILEMODE) != -1); + assumes(touch_file(_PATH_VARRUN "/.systemStarterRunning", DEFFILEMODE) != -1); + + if (!path_check("/var/db/netinfo/local.nidb.migrated") && !path_check("/var/db/netinfo/local.nidb")) { + const char *create_nidb_tool[] = { "/usr/libexec/create_nidb", NULL }; + + fprintf(stderr, "NetInfo database missing. Creating...\n"); + + mkdir("/var/db/netinfo", ACCESSPERMS); + remove("/var/db/.AppleSetupDone"); + assumes(fwexec(create_nidb_tool, true) != -1); + } + + if (path_check("/etc/security/rc.audit")) { + const char *audit_tool[] = { _PATH_BSHELL, "/etc/security/rc.audit", NULL }; + assumes(fwexec(audit_tool, true) != -1); + } + + if (path_check("/Library/Preferences/com.apple.sharing.firewall.plist")) { + const char *fw_tool[] = { "/usr/libexec/FirewallTool", NULL }; + assumes(fwexec(fw_tool, true) != -1); + } + + const char *bcc_tool[] = { "BootCacheControl", "start", NULL }; + assumes(fwexec(bcc_tool, true) != -1); + + char *load_launchd_items[] = { "load", "-D", "all", "/etc/mach_init.d", NULL }; + if (is_safeboot()) + load_launchd_items[2] = "system"; + assumes(load_and_unload_cmd(4, load_launchd_items) == 0); + + const char *bcc_tag_tool[] = { "BootCacheControl", "tag", NULL }; + assumes(fwexec(bcc_tag_tool, true) != -1); + + const char *SystemStarter_tool[] = { "SystemStarter", NULL }; + assumes(fwexec(SystemStarter_tool, false) != -1); + + workaround4465949(); + + if (path_check("/etc/rc.local")) { + const char *rc_local_tool[] = { _PATH_BSHELL, "/etc/rc.local", NULL }; + assumes(fwexec(rc_local_tool, false) != -1); + } + + return 0; +} + +void +workaround4465949(void) +{ + const char *pbs_tool[] = { "/System/Library/CoreServices/pbs", NULL }; + const char *lca_tool[] = { "/System/Library/CoreServices/Language Chooser.app/Contents/MacOS/Language Chooser", NULL}; + char *const reloadttys_argv[] = { "reloadttys", NULL }; + int wstatus; + pid_t pbs_p; + + if (path_check("/System/Library/LaunchDaemons/com.apple.loginwindow.plist")) { + return; + } + + if (path_check(pbs_tool[0]) && path_check(lca_tool[0]) && + !path_check("/var/db/.AppleSetupDone") && + path_check("/var/db/.RunLanguageChooserToo")) { + if (assumes((pbs_p = fwexec(pbs_tool, false)) != -1)) { + assumes(fwexec(lca_tool, true) != -1); + assumes(kill(pbs_p, SIGTERM) != -1); + assumes(waitpid(pbs_p, &wstatus, 0) != -1); + } + } + + assumes(fyi_cmd(1, reloadttys_argv) == 0); +} + +int +load_and_unload_cmd(int argc, char *const argv[]) +{ + NSSearchPathEnumerationState es = 0; + char nspath[PATH_MAX * 2]; /* safe side, we need to append */ + bool badopts = false; + struct load_unload_state lus; + size_t i; + int ch; + + memset(&lus, 0, sizeof(lus)); if (!strcmp(argv[0], "load")) - lflag = true; + lus.load = true; - while ((ch = getopt(argc, argv, "wF")) != -1) { + while ((ch = getopt(argc, argv, "wFS:D:")) != -1) { switch (ch) { - case 'w': wflag = true; break; - case 'F': Fflag = true; break; + case 'w': + lus.editondisk = true; + break; + case 'F': + lus.forceload = true; + break; + case 'S': + lus.session_type = optarg; + break; + case 'D': + if (strcasecmp(optarg, "all") == 0) { + es |= NSAllDomainsMask; + } else if (strcasecmp(optarg, "user") == 0) { + es |= NSUserDomainMask; + } else if (strcasecmp(optarg, "local") == 0) { + es |= NSLocalDomainMask; + } else if (strcasecmp(optarg, "network") == 0) { + es |= NSNetworkDomainMask; + } else if (strcasecmp(optarg, "system") == 0) { + es |= NSSystemDomainMask; + } else { + badopts = true; + } + break; + case '?': default: - fprintf(stderr, "usage: %s load [-wF] paths...\n", getprogname()); - return 1; + badopts = true; + break; } } argc -= optind; argv += optind; - if (argc == 0) { - fprintf(stderr, "usage: %s load [-w] paths...\n", getprogname()); + if (lus.session_type == NULL) + es &= ~NSUserDomainMask; + + if (argc == 0 && es == 0) + badopts = true; + + if (badopts) { + fprintf(stderr, "usage: %s load [-wF] [-D ] paths...\n", getprogname()); return 1; } - /* I wish I didn't need to do multiple passes, but I need to load mDNSResponder and use it too. + /* I wish I didn't need to do three passes, but I need to load mDNSResponder and use it too. * And loading legacy mach init jobs is extra fun. * * In later versions of launchd, I hope to load everything in the first pass, @@ -961,37 +1363,59 @@ static int load_and_unload_cmd(int argc, char *const argv[]) * launchd doesn't have reload support right now. */ - pass0 = launch_data_alloc(LAUNCH_DATA_ARRAY); - pass1 = launch_data_alloc(LAUNCH_DATA_ARRAY); + lus.pass0 = launch_data_alloc(LAUNCH_DATA_ARRAY); + lus.pass1 = launch_data_alloc(LAUNCH_DATA_ARRAY); + lus.pass2 = launch_data_alloc(LAUNCH_DATA_ARRAY); - for (i = 0; i < argc; i++) - readpath(argv[i], pass0, pass1, wflag, lflag, Fflag); + es = NSStartSearchPathEnumeration(NSLibraryDirectory, es); + + while ((es = NSGetNextSearchPathEnumeration(es, nspath))) { + glob_t g; + + if (lus.session_type) { + strcat(nspath, "/LaunchAgents"); + } else { + strcat(nspath, "/LaunchDaemons"); + } - if (launch_data_array_get_count(pass0) == 0 && - launch_data_array_get_count(pass1) == 0) { - fprintf(stderr, "nothing found to %s\n", lflag ? "load" : "unload"); - launch_data_free(pass0); - launch_data_free(pass1); + if (glob(nspath, GLOB_TILDE|GLOB_NOSORT, NULL, &g) == 0) { + for (i = 0; i < g.gl_pathc; i++) { + readpath(g.gl_pathv[i], &lus); + } + globfree(&g); + } + } + + for (i = 0; i < (size_t)argc; i++) + readpath(argv[i], &lus); + + if (launch_data_array_get_count(lus.pass0) == 0 && + launch_data_array_get_count(lus.pass1) == 0 && + launch_data_array_get_count(lus.pass2) == 0) { + fprintf(stderr, "nothing found to %s\n", lus.load ? "load" : "unload"); + launch_data_free(lus.pass0); + launch_data_free(lus.pass1); + launch_data_free(lus.pass2); return 1; } - if (lflag) { - distill_jobs(pass1); - submit_mach_jobs(pass0); - workaround_bonjour_asynchronously(); - submit_job_pass(pass1); - let_go_of_mach_jobs(); + if (lus.load) { + distill_jobs(lus.pass1); + submit_mach_jobs(lus.pass0); + submit_job_pass(lus.pass1); + let_go_of_mach_jobs(lus.pass0); + distill_jobs(lus.pass2); + submit_job_pass(lus.pass2); } else { - for (i = 0; i < (int)launch_data_array_get_count(pass1); i++) - unloadjob(launch_data_array_get_index(pass1, i)); + for (i = 0; i < launch_data_array_get_count(lus.pass1); i++) + unloadjob(launch_data_array_get_index(lus.pass1, i)); + for (i = 0; i < launch_data_array_get_count(lus.pass2); i++) + unloadjob(launch_data_array_get_index(lus.pass2, i)); } return 0; } -static mach_port_t *msrvs = NULL; -static size_t msrvs_cnt = 0; - void submit_mach_jobs(launch_data_t jobs) { @@ -999,21 +1423,16 @@ submit_mach_jobs(launch_data_t jobs) c = launch_data_array_get_count(jobs); - msrvs = calloc(1, sizeof(mach_port_t) * c); - msrvs_cnt = c; - for (i = 0; i < c; i++) { launch_data_t tmp, oai = launch_data_array_get_index(jobs, i); const char *sn = NULL, *cmd = NULL; - bool d = true, k = false; - mach_port_t msr, msv, mhp; + bool d = true; + mach_port_t msr, msv; kern_return_t kr; uid_t u = getuid(); if ((tmp = launch_data_dict_lookup(oai, MACHINIT_JOBKEY_ONDEMAND))) d = launch_data_get_bool(tmp); - if ((tmp = launch_data_dict_lookup(oai, MACHINIT_JOBKEY_ISKUNCSERVER))) - k = launch_data_get_bool(tmp); if ((tmp = launch_data_dict_lookup(oai, MACHINIT_JOBKEY_SERVICENAME))) sn = launch_data_get_string(tmp); if ((tmp = launch_data_dict_lookup(oai, MACHINIT_JOBKEY_COMMAND))) @@ -1028,24 +1447,29 @@ submit_mach_jobs(launch_data_t jobs) mach_port_destroy(mach_task_self(), msr); continue; } - if (k) { - mhp = mach_host_self(); - if ((kr = host_set_UNDServer(mhp, msv)) != KERN_SUCCESS) - fprintf(stderr, "%s: host_set_UNDServer(): %s\n", getprogname(), mach_error_string(kr)); - mach_port_deallocate(mach_task_self(), mhp); - } - mach_port_deallocate(mach_task_self(), msv); - msrvs[i] = msr; + launch_data_dict_insert(oai, launch_data_new_machport(msr), MACHINIT_JOBKEY_SERVERPORT); + launch_data_dict_insert(oai, launch_data_new_machport(msv), MACHINIT_JOBKEY_SERVICEPORT); } } void -let_go_of_mach_jobs(void) +let_go_of_mach_jobs(launch_data_t jobs) { - size_t i; + size_t i, c = launch_data_array_get_count(jobs); - for (i = 0; i < msrvs_cnt; i++) - mach_port_destroy(mach_task_self(), msrvs[i]); + for (i = 0; i < c; i++) { + launch_data_t tmp, oai = launch_data_array_get_index(jobs, i); + if ((tmp = launch_data_dict_lookup(oai, MACHINIT_JOBKEY_SERVICEPORT))) { + mach_port_destroy(mach_task_self(), launch_data_get_machport(tmp)); + } else { + fprintf(stderr, "%s: ack! missing service port!\n", getprogname()); + } + if ((tmp = launch_data_dict_lookup(oai, MACHINIT_JOBKEY_SERVERPORT))) { + mach_port_destroy(mach_task_self(), launch_data_get_machport(tmp)); + } else { + fprintf(stderr, "%s: ack! missing server port!\n", getprogname()); + } + } } void @@ -1104,15 +1528,19 @@ submit_job_pass(launch_data_t jobs) launch_data_free(msg); } -static int start_and_stop_cmd(int argc, char *const argv[]) +int +start_stop_remove_cmd(int argc, char *const argv[]) { launch_data_t resp, msg; const char *lmsgcmd = LAUNCH_KEY_STOPJOB; int e, r = 0; - if (!strcmp(argv[0], "start")) + if (0 == strcmp(argv[0], "start")) lmsgcmd = LAUNCH_KEY_STARTJOB; + if (0 == strcmp(argv[0], "remove")) + lmsgcmd = LAUNCH_KEY_REMOVEJOB; + if (argc != 2) { fprintf(stderr, "usage: %s %s \n", getprogname(), argv[0]); return 1; @@ -1141,34 +1569,122 @@ static int start_and_stop_cmd(int argc, char *const argv[]) return r; } -static void print_jobs(launch_data_t j __attribute__((unused)), const char *label, void *context __attribute__((unused))) +void +print_jobs(launch_data_t j) { + static size_t depth = 0; + launch_data_t lo = launch_data_dict_lookup(j, LAUNCH_JOBKEY_LABEL); + launch_data_t pido = launch_data_dict_lookup(j, LAUNCH_JOBKEY_PID); + launch_data_t stato = launch_data_dict_lookup(j, LAUNCH_JOBKEY_LASTEXITSTATUS); + launch_data_t sjobs = launch_data_dict_lookup(j, LAUNCH_JOBKEY_SUBJOBS); + const char *label = launch_data_get_string(lo); + size_t i, c; + + if (pido) { + fprintf(stdout, "%lld\t-\t", launch_data_get_integer(pido)); + } else if (stato) { + int wstatus = (int)launch_data_get_integer(stato); + if (WIFEXITED(wstatus)) { + fprintf(stdout, "-\t%d\t", WEXITSTATUS(wstatus)); + } else if (WIFSIGNALED(wstatus)) { + fprintf(stdout, "-\t-%d\t", WTERMSIG(wstatus)); + } else { + fprintf(stdout, "-\t???\t"); + } + } else { + fprintf(stdout, "-\t-\t"); + } + for (i = 0; i < depth; i++) + fprintf(stdout, "\t"); + fprintf(stdout, "%s\n", label); + + if (sjobs) { + launch_data_t oai; + + c = launch_data_array_get_count(sjobs); + + depth++; + for (i = 0; i < c; i++) { + oai = launch_data_array_get_index(sjobs, i); + print_jobs(oai); + } + depth--; + } } -static int list_cmd(int argc, char *const argv[]) +void +print_obj(launch_data_t obj, const char *key, void *context __attribute__((unused))) { - launch_data_t resp, msg; - int ch, r = 0; - bool vflag = false; + static size_t indent = 0; + size_t i, c; - while ((ch = getopt(argc, argv, "v")) != -1) { - switch (ch) { - case 'v': - vflag = true; - break; - default: - fprintf(stderr, "usage: %s list [-v]\n", getprogname()); - return 1; - } + for (i = 0; i < indent; i++) + fprintf(stdout, "\t"); + + if (key) + fprintf(stdout, "\"%s\" = ", key); + + switch (launch_data_get_type(obj)) { + case LAUNCH_DATA_STRING: + fprintf(stdout, "\"%s\";\n", launch_data_get_string(obj)); + break; + case LAUNCH_DATA_INTEGER: + fprintf(stdout, "%lld;\n", launch_data_get_integer(obj)); + break; + case LAUNCH_DATA_REAL: + fprintf(stdout, "%f;\n", launch_data_get_real(obj)); + break; + case LAUNCH_DATA_BOOL: + fprintf(stdout, "%s;\n", launch_data_get_bool(obj) ? "true" : "false"); + break; + case LAUNCH_DATA_ARRAY: + c = launch_data_array_get_count(obj); + fprintf(stdout, "(\n"); + indent++; + for (i = 0; i < c; i++) + print_obj(launch_data_array_get_index(obj, i), NULL, NULL); + indent--; + for (i = 0; i < indent; i++) + fprintf(stdout, "\t"); + fprintf(stdout, ");\n"); + break; + case LAUNCH_DATA_DICTIONARY: + fprintf(stdout, "{\n"); + indent++; + launch_data_dict_iterate(obj, print_obj, NULL); + indent--; + for (i = 0; i < indent; i++) + fprintf(stdout, "\t"); + fprintf(stdout, "};\n"); + break; + case LAUNCH_DATA_FD: + fprintf(stdout, "file-descriptor-object;\n"); + break; + case LAUNCH_DATA_MACHPORT: + fprintf(stdout, "mach-port-object;\n"); + break; + default: + fprintf(stdout, "???;\n"); + break; } +} - if (vflag) { - fprintf(stderr, "usage: %s list: \"-v\" flag not implemented yet\n", getprogname()); +int +list_cmd(int argc, char *const argv[]) +{ + launch_data_t resp, msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY); + int r = 0; + + if (argc > 2) { + fprintf(stderr, "usage: %s list [label]\n", getprogname()); return 1; + } else if (argc == 2) { + launch_data_dict_insert(msg, launch_data_new_string(argv[1]), LAUNCH_KEY_GETJOB); + } else { + launch_data_dict_insert(msg, launch_data_new_string(""), LAUNCH_KEY_GETJOB); } - msg = launch_data_new_string(LAUNCH_KEY_GETJOBS); resp = launch_msg(msg); launch_data_free(msg); @@ -1176,7 +1692,12 @@ static int list_cmd(int argc, char *const argv[]) fprintf(stderr, "launch_msg(): %s\n", strerror(errno)); return 1; } else if (launch_data_get_type(resp) == LAUNCH_DATA_DICTIONARY) { - launch_data_dict_iterate(resp, print_jobs, NULL); + if (argc == 1) { + fprintf(stdout, "PID\tStatus\tLabel\n"); + print_jobs(resp); + } else { + print_obj(resp, NULL, NULL); + } } else { fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]); r = 1; @@ -1187,7 +1708,8 @@ static int list_cmd(int argc, char *const argv[]) return r; } -static int stdio_cmd(int argc, char *const argv[]) +int +stdio_cmd(int argc, char *const argv[]) { launch_data_t resp, msg, tmp; int e, fd = -1, r = 0; @@ -1197,7 +1719,7 @@ static int stdio_cmd(int argc, char *const argv[]) return 1; } - fd = open(argv[1], O_CREAT|O_APPEND|O_WRONLY, DEFFILEMODE); + fd = open(argv[1], O_CREAT|O_APPEND|O_WRONLY|O_NOCTTY, DEFFILEMODE); msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY); @@ -1237,7 +1759,8 @@ static int stdio_cmd(int argc, char *const argv[]) return r; } -static int fyi_cmd(int argc, char *const argv[]) +int +fyi_cmd(int argc, char *const argv[]) { launch_data_t resp, msg; const char *lmsgk = LAUNCH_KEY_RELOADTTYS; @@ -1248,8 +1771,11 @@ static int fyi_cmd(int argc, char *const argv[]) return 1; } - if (!strcmp(argv[0], "shutdown")) + if (!strcmp(argv[0], "shutdown")) { lmsgk = LAUNCH_KEY_SHUTDOWN; + } else if (!strcmp(argv[0], "singleuser")) { + lmsgk = LAUNCH_KEY_SINGLEUSER; + } msg = launch_data_new_string(lmsgk); resp = launch_msg(msg); @@ -1273,7 +1799,8 @@ static int fyi_cmd(int argc, char *const argv[]) return r; } -static int logupdate_cmd(int argc, char *const argv[]) +int +logupdate_cmd(int argc, char *const argv[]) { launch_data_t resp, msg; int e, i, j, r = 0, m = 0; @@ -1398,7 +1925,8 @@ static const struct { static const size_t limlookupcnt = sizeof limlookup / sizeof limlookup[0]; -static ssize_t name2num(const char *n) +ssize_t +name2num(const char *n) { size_t i; @@ -1410,7 +1938,8 @@ static ssize_t name2num(const char *n) return -1; } -static const char *num2name(int n) +const char * +num2name(int n) { size_t i; @@ -1421,7 +1950,8 @@ static const char *num2name(int n) return NULL; } -static const char *lim2str(rlim_t val, char *buf) +const char * +lim2str(rlim_t val, char *buf) { if (val == RLIM_INFINITY) strcpy(buf, "unlimited"); @@ -1430,7 +1960,8 @@ static const char *lim2str(rlim_t val, char *buf) return buf; } -static bool str2lim(const char *buf, rlim_t *res) +bool +str2lim(const char *buf, rlim_t *res) { char *endptr; *res = strtoll(buf, &endptr, 10); @@ -1443,7 +1974,8 @@ static bool str2lim(const char *buf, rlim_t *res) return true; } -static int limit_cmd(int argc __attribute__((unused)), char *const argv[]) +int +limit_cmd(int argc __attribute__((unused)), char *const argv[]) { char slimstr[100]; char hlimstr[100]; @@ -1537,7 +2069,8 @@ static int limit_cmd(int argc __attribute__((unused)), char *const argv[]) return r; } -static int umask_cmd(int argc, char *const argv[]) +int +umask_cmd(int argc, char *const argv[]) { launch_data_t resp, msg; bool badargs = false; @@ -1584,7 +2117,69 @@ static int umask_cmd(int argc, char *const argv[]) return r; } -static int getrusage_cmd(int argc, char *const argv[]) +int +submit_cmd(int argc, char *const argv[]) +{ + launch_data_t msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY); + launch_data_t job = launch_data_alloc(LAUNCH_DATA_DICTIONARY); + launch_data_t resp, largv = launch_data_alloc(LAUNCH_DATA_ARRAY); + int ch, i, r = 0; + + launch_data_dict_insert(job, launch_data_new_bool(false), LAUNCH_JOBKEY_ONDEMAND); + + while ((ch = getopt(argc, argv, "l:p:o:e:")) != -1) { + switch (ch) { + case 'l': + launch_data_dict_insert(job, launch_data_new_string(optarg), LAUNCH_JOBKEY_LABEL); + break; + case 'p': + launch_data_dict_insert(job, launch_data_new_string(optarg), LAUNCH_JOBKEY_PROGRAM); + break; + case 'o': + launch_data_dict_insert(job, launch_data_new_string(optarg), LAUNCH_JOBKEY_STANDARDOUTPATH); + break; + case 'e': + launch_data_dict_insert(job, launch_data_new_string(optarg), LAUNCH_JOBKEY_STANDARDERRORPATH); + break; + default: + fprintf(stderr, "usage: %s submit ...\n", getprogname()); + return 1; + } + } + argc -= optind; + argv += optind; + + for (i = 0; argv[i]; i++) { + launch_data_array_append(largv, launch_data_new_string(argv[i])); + } + + launch_data_dict_insert(job, largv, LAUNCH_JOBKEY_PROGRAMARGUMENTS); + + launch_data_dict_insert(msg, job, LAUNCH_KEY_SUBMITJOB); + + resp = launch_msg(msg); + launch_data_free(msg); + + if (resp == NULL) { + fprintf(stderr, "launch_msg(): %s\n", strerror(errno)); + return 1; + } else if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) { + errno = launch_data_get_errno(resp); + if (errno) { + fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], strerror(errno)); + r = 1; + } + } else { + fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], "unknown response"); + } + + launch_data_free(resp); + + return r; +} + +int +getrusage_cmd(int argc, char *const argv[]) { launch_data_t resp, msg; bool badargs = false; @@ -1645,13 +2240,116 @@ static int getrusage_cmd(int argc, char *const argv[]) return r; } -static bool launch_data_array_append(launch_data_t a, launch_data_t o) +bool +launch_data_array_append(launch_data_t a, launch_data_t o) { size_t offt = launch_data_array_get_count(a); return launch_data_array_set_index(a, o, offt); } +mach_port_t +str2bsport(const char *s) +{ + bool getrootbs = strcmp(s, "/") == 0; + mach_port_t last_bport, bport = bootstrap_port; + task_t task = mach_task_self(); + kern_return_t result; + + if (strcmp(s, "..") == 0 || getrootbs) { + do { + last_bport = bport; + result = bootstrap_parent(last_bport, &bport); + + if (result == BOOTSTRAP_NOT_PRIVILEGED) { + fprintf(stderr, "Permission denied\n"); + return 1; + } else if (result != BOOTSTRAP_SUCCESS) { + fprintf(stderr, "bootstrap_parent() %d\n", result); + return 1; + } + } while (getrootbs && last_bport != bport); + } else { + int pid = atoi(s); + + result = task_for_pid(mach_task_self(), pid, &task); + + if (result != KERN_SUCCESS) { + fprintf(stderr, "task_for_pid() %s\n", mach_error_string(result)); + return 1; + } + + result = task_get_bootstrap_port(task, &bport); + + if (result != KERN_SUCCESS) { + fprintf(stderr, "Couldn't get bootstrap port: %s\n", mach_error_string(result)); + return 1; + } + } + + return bport; +} + +int +bsexec_cmd(int argc, char *const argv[]) +{ + kern_return_t result; + mach_port_t bport; + + if (argc < 3) { + fprintf(stderr, "usage: %s bsexec prog...\n", getprogname()); + return 1; + } + + bport = str2bsport(argv[1]); + + result = task_set_bootstrap_port(mach_task_self(), bport); + + if (result != KERN_SUCCESS) { + fprintf(stderr, "Couldn't switch to new bootstrap port: %s\n", mach_error_string(result)); + return 1; + } + + setgid(getgid()); + setuid(getuid()); + + execvp(argv[2], argv + 2); + fprintf(stderr, "execvp(): %s\n", strerror(errno)); + return 1; +} + +int +bslist_cmd(int argc, char *const argv[]) +{ + kern_return_t result; + mach_port_t bport = bootstrap_port; + name_array_t service_names; + mach_msg_type_number_t service_cnt, service_active_cnt; + bootstrap_status_array_t service_actives; + unsigned int i; + + if (argc == 2) + bport = str2bsport(argv[1]); + + if (bport == MACH_PORT_NULL) { + fprintf(stderr, "Invalid bootstrap port\n"); + return 1; + } + + result = bootstrap_info(bport, &service_names, &service_cnt, &service_actives, &service_active_cnt); + if (result != BOOTSTRAP_SUCCESS) { + fprintf(stderr, "bootstrap_info(): %d\n", result); + return 1; + } + +#define bport_state(x) (((x) == BOOTSTRAP_STATUS_ACTIVE) ? "A" : ((x) == BOOTSTRAP_STATUS_ON_DEMAND) ? "D" : "I") + + for (i = 0; i < service_cnt ; i++) + fprintf(stdout, "%-3s%s\n", bport_state((service_actives[i])), service_names[i]); + + return 0; +} + bool is_legacy_mach_job(launch_data_t obj) { @@ -1661,3 +2359,315 @@ is_legacy_mach_job(launch_data_t obj) return has_command && has_servicename && !has_label; } + +void +_log_launchctl_bug(const char *rcs_rev, const char *path, unsigned int line, const char *test) +{ + int saved_errno = errno; + char buf[100]; + const char *file = strrchr(path, '/'); + char *rcs_rev_tmp = strchr(rcs_rev, ' '); + + if (!file) { + file = path; + } else { + file += 1; + } + + if (!rcs_rev_tmp) { + strlcpy(buf, rcs_rev, sizeof(buf)); + } else { + strlcpy(buf, rcs_rev_tmp + 1, sizeof(buf)); + rcs_rev_tmp = strchr(buf, ' '); + if (rcs_rev_tmp) + *rcs_rev_tmp = '\0'; + } + + fprintf(stderr, "Bug: %s:%u (%s):%u: %s\n", file, line, buf, saved_errno, test); +} + +void +loopback_setup_ipv4(void) +{ + struct ifaliasreq ifra; + struct ifreq ifr; + int s; + + memset(&ifr, 0, sizeof(ifr)); + strcpy(ifr.ifr_name, "lo0"); + + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) + return; + + if (assumes(ioctl(s, SIOCGIFFLAGS, &ifr) != -1)) { + ifr.ifr_flags |= IFF_UP; + assumes(ioctl(s, SIOCSIFFLAGS, &ifr) != -1); + } + + memset(&ifra, 0, sizeof(ifra)); + strcpy(ifra.ifra_name, "lo0"); + ((struct sockaddr_in *)&ifra.ifra_addr)->sin_family = AF_INET; + ((struct sockaddr_in *)&ifra.ifra_addr)->sin_addr.s_addr = htonl(INADDR_LOOPBACK); + ((struct sockaddr_in *)&ifra.ifra_addr)->sin_len = sizeof(struct sockaddr_in); + ((struct sockaddr_in *)&ifra.ifra_mask)->sin_family = AF_INET; + ((struct sockaddr_in *)&ifra.ifra_mask)->sin_addr.s_addr = htonl(IN_CLASSA_NET); + ((struct sockaddr_in *)&ifra.ifra_mask)->sin_len = sizeof(struct sockaddr_in); + + assumes(ioctl(s, SIOCAIFADDR, &ifra) != -1); + + assumes(close(s) == 0); +} + +void +loopback_setup_ipv6(void) +{ + struct in6_aliasreq ifra6; + struct ifreq ifr; + int s6; + + memset(&ifr, 0, sizeof(ifr)); + strcpy(ifr.ifr_name, "lo0"); + + if ((s6 = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) + return; + + memset(&ifr, 0, sizeof(ifr)); + strcpy(ifr.ifr_name, "lo0"); + + if (assumes(ioctl(s6, SIOCGIFFLAGS, &ifr) != -1)) { + ifr.ifr_flags |= IFF_UP; + assumes(ioctl(s6, SIOCSIFFLAGS, &ifr) != -1); + } + + memset(&ifra6, 0, sizeof(ifra6)); + strcpy(ifra6.ifra_name, "lo0"); + + ifra6.ifra_addr.sin6_family = AF_INET6; + ifra6.ifra_addr.sin6_addr = in6addr_loopback; + ifra6.ifra_addr.sin6_len = sizeof(struct sockaddr_in6); + ifra6.ifra_prefixmask.sin6_family = AF_INET6; + memset(&ifra6.ifra_prefixmask.sin6_addr, 0xff, sizeof(struct in6_addr)); + ifra6.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6); + ifra6.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME; + ifra6.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME; + + assumes(ioctl(s6, SIOCAIFADDR_IN6, &ifra6) != -1); + + assumes(close(s6) == 0); +} + +pid_t +fwexec(const char *const *argv, bool _wait) +{ + int wstatus; + pid_t p; + + switch ((p = fork())) { + case -1: + break; + case 0: + setsid(); + execvp(argv[0], (char *const *)argv); + _exit(EXIT_FAILURE); + break; + default: + if (!_wait) + return p; + if (p == waitpid(p, &wstatus, 0)) { + if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == EXIT_SUCCESS) + return p; + } + break; + } + + return -1; +} + +void +do_potential_fsck(void) +{ + const char *safe_fsck_tool[] = { "fsck", "-fy", NULL }; + const char *fsck_tool[] = { "fsck", "-p", NULL }; + const char *remount_tool[] = { "mount", "-uw", "/", NULL }; + struct statfs sfs; + + if (assumes(statfs("/", &sfs) != -1)) { + if (!(sfs.f_flags & MNT_RDONLY)) { + fprintf(stdout, "Root file system is read-write, skipping fsck.\n"); + return; + } + } + + if (!is_safeboot()) { + if (fwexec(fsck_tool, true) != -1) + goto out; + } + + if (fwexec(safe_fsck_tool, true) != -1) { + goto out; + } + + fprintf(stderr, "fsck failed! Leaving the root file system read-only...\n"); + + return; +out: + assumes(fwexec(remount_tool, true) != -1); +} + +bool +path_check(const char *path) +{ + struct stat sb; + + if (stat(path, &sb) == 0) + return true; + return false; +} + +bool +is_safeboot(void) +{ + int sbmib[] = { CTL_KERN, KERN_SAFEBOOT }; + uint32_t sb = 0; + size_t sbsz = sizeof(sb); + + if (!assumes(sysctl(sbmib, 2, &sb, &sbsz, NULL, 0) == 0)) + return false; + + return (bool)sb; +} + +bool +is_netboot(void) +{ + int nbmib[] = { CTL_KERN, KERN_NETBOOT }; + uint32_t nb = 0; + size_t nbsz = sizeof(nb); + + if (!assumes(sysctl(nbmib, 2, &nb, &nbsz, NULL, 0) == 0)) + return false; + + return (bool)nb; +} + +void +apply_func_to_dir(const char *thedir, void (*thefunc)(const char *)) +{ + struct dirent *de; + DIR *od; + int currend_dir_fd; + + if (!assumes((currend_dir_fd = open(".", 0)) != -1)) + return; + + if (!assumes(chdir(thedir) != -1)) + goto out; + + if (!assumes(od = opendir("."))) + goto out; + + while ((de = readdir(od))) { + struct stat sb; + + if (strcmp(de->d_name, ".") == 0) + continue; + if (strcmp(de->d_name, "..") == 0) + continue; + + if (assumes(stat(de->d_name, &sb) != -1)) { + if (S_ISDIR(sb.st_mode)) + apply_func_to_dir(de->d_name, thefunc); + thefunc(de->d_name); + } + } + + assumes(closedir(od) != -1); + +out: + assumes(fchdir(currend_dir_fd) != -1); + assumes(close(currend_dir_fd) != -1); +} + +void +empty_dir(const char *path) +{ + assumes(chflags(path, 0) != -1); + assumes(remove(path) != -1); +} + +int +touch_file(const char *path, mode_t m) +{ + int fd = open(path, O_CREAT, m); + + if (fd == -1) + return -1; + + return close(fd); +} + +void +apply_sysctls_from_file(const char *thefile) +{ + const char *sysctl_tool[] = { "sysctl", "-w", NULL, NULL }; + size_t ln_len = 0; + char *val, *tmpstr; + FILE *sf; + + if (!(sf = fopen(thefile, "r"))) + return; + + while ((val = fgetln(sf, &ln_len))) { + if (ln_len == 0) + continue; + if (!assumes((tmpstr = malloc(ln_len + 1)) != NULL)) + continue; + memcpy(tmpstr, val, ln_len); + tmpstr[ln_len] = 0; + val = tmpstr; + + while (*val && isspace(*val)) + val++; + if (*val == '\0' || *val == '#') + goto skip_sysctl_tool; + sysctl_tool[2] = val; + assumes(fwexec(sysctl_tool, true) != -1); +skip_sysctl_tool: + free(tmpstr); + } + + assumes(fclose(sf) == 0); +} + +void +do_sysversion_sysctl(void) +{ + int mib[] = { CTL_KERN, KERN_OSVERSION }; + CFDictionaryRef versdict; + CFStringRef buildvers; + char buf[1024]; + size_t bufsz = sizeof(buf); + + /* ER: launchd should set kern.osversion very early in boot */ + + if (getuid() != 0) + return; + + if (sysctl(mib, 2, buf, &bufsz, NULL, 0) == -1) { + fprintf(stderr, "sysctl(): %s\n", strerror(errno)); + return; + } + + if (buf[0] != '\0') + return; + + versdict = _CFCopySystemVersionDictionary(); + buildvers = CFDictionaryGetValue(versdict, _kCFSystemVersionBuildVersionKey); + CFStringGetCString(buildvers, buf, sizeof(buf), kCFStringEncodingUTF8); + + if (sysctl(mib, 2, NULL, 0, buf, strlen(buf) + 1) == -1) { + fprintf(stderr, "sysctl(): %s\n", strerror(errno)); + } + + CFRelease(versdict); +} diff --git a/launchd/src/launchd.8 b/launchd/src/launchd.8 index 5938eae..30e9145 100644 --- a/launchd/src/launchd.8 +++ b/launchd/src/launchd.8 @@ -7,15 +7,20 @@ .Sh SYNOPSIS .Nm .Op Fl d -.Op Fl v +.Op Fl D .Op Fl s -.Op Fl x +.Op Fl S Ar SessionType .Op Ar -- command Op Ar args ... .Sh DESCRIPTION .Nm -manages daemons, both for the system as a whole and for individual users. Ideal daemons can launch -on demand based on criteria specified in their respective XML property lists located in one of the -directories specified in the FILES section. +manages processes, both for the system as a whole and for individual users. +The primary and preferred interface to +.Nm +is via the +.Xr launchctl 1 +tool which (among other options) allows the user or administrator to load and unload jobs. +Where possible, it is preferable for jobs to launch on demand based on criteria specified +in their respective configuration files. .Pp When run with a command, a specific instance of .Nm @@ -30,18 +35,23 @@ instance of During boot .Nm is invoked by the kernel to run as the first process on the system and to further bootstrap the rest of the system. -.Sh OPTIONS WHEN RUN AS PID 1 +.Sh OPTIONS .Bl -tag -width -indent .It Fl d Daemonize. Useful when passing a command to launchd on the command line. -.It Fl v -Verbose boot. +.It Fl D +Debug. Prints syslog messages to stderr and adjusts logging via +.Xr syslog 3 +to LOG_DEBUG. +.It Fl S Ar SessionType +Instruct launchd to which session type it is. +.El +.Sh OPTIONS WHEN RUN AS PID 1 +.Bl -tag -width -indent .It Fl s Single user mode. Instructs .Nm launchd to give a shell prompt before booting the system. -.It Fl x -Safe mode boot. Instructs the system to boot conservatively. .El .Sh ENVIRONMENTAL VARIABLES .Bl -tag -width -indent diff --git a/launchd/src/launchd.c b/launchd/src/launchd.c index 0a714e1..e7acf68 100644 --- a/launchd/src/launchd.c +++ b/launchd/src/launchd.c @@ -1,25 +1,25 @@ /* * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ + * @APPLE_APACHE_LICENSE_HEADER_START@ * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * The 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, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and * limitations under the License. * - * @APPLE_LICENSE_HEADER_END@ + * @APPLE_APACHE_LICENSE_HEADER_END@ */ + +static const char *const __rcs_file_version__ = "$Revision: 1.217 $"; + #include #include #include @@ -37,10 +37,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -50,176 +52,164 @@ #include #include #include -#include #include #include #include +#include #include #include +#include +#include +#include "bootstrap_public.h" +#include "bootstrap_private.h" #include "launch.h" #include "launch_priv.h" #include "launchd.h" +#include "launchd_core_logic.h" +#include "launchd_unix_ipc.h" + +#include "launchd_internalServer.h" +#include "launchd_internal.h" +#include "notifyServer.h" +#include "bootstrapServer.h" + +union MaxRequestSize { + union __RequestUnion__do_notify_subsystem req; + union __ReplyUnion__do_notify_subsystem rep; + union __RequestUnion__x_launchd_internal_subsystem req2; + union __ReplyUnion__x_launchd_internal_subsystem rep2; + union __RequestUnion__x_bootstrap_subsystem req3; + union __ReplyUnion__x_bootstrap_subsystem rep3; +}; -#include "bootstrap_internal.h" +static boolean_t launchd_internal_demux(mach_msg_header_t *Request, mach_msg_header_t *Reply); -#define LAUNCHD_MIN_JOB_RUN_TIME 10 -#define LAUNCHD_REWARD_JOB_RUN_TIME 60 -#define LAUNCHD_FAILED_EXITS_THRESHOLD 10 #define PID1LAUNCHD_CONF "/etc/launchd.conf" #define LAUNCHD_CONF ".launchd.conf" #define LAUNCHCTL_PATH "/bin/launchctl" #define SECURITY_LIB "/System/Library/Frameworks/Security.framework/Versions/A/Security" -#define VOLFSDIR "/.vol" extern char **environ; -struct jobcb { - kq_callback kqjob_callback; - TAILQ_ENTRY(jobcb) tqe; - launch_data_t ldj; - pid_t p; - int execfd; - time_t start_time; - size_t failed_exits; - int *vnodes; - size_t vnodes_cnt; - int *qdirs; - size_t qdirs_cnt; - unsigned int start_interval; - struct tm *start_cal_interval; - unsigned int checkedin:1, firstborn:1, debug:1, throttle:1, futureflags:28; - char label[0]; -}; - -struct conncb { - kq_callback kqconn_callback; - TAILQ_ENTRY(conncb) tqe; - launch_t conn; - struct jobcb *j; - int disabled_batch:1, futureflags:31; -}; - -static TAILQ_HEAD(jobcbhead, jobcb) jobs = TAILQ_HEAD_INITIALIZER(jobs); -static TAILQ_HEAD(conncbhead, conncb) connections = TAILQ_HEAD_INITIALIZER(connections); -static int mainkq = 0; -static int asynckq = 0; -static int batch_disabler_count = 0; - -static launch_data_t load_job(launch_data_t pload); -static launch_data_t get_jobs(const char *which); -static launch_data_t setstdio(int d, launch_data_t o); -static launch_data_t adjust_rlimits(launch_data_t in); -static void batch_job_enable(bool e, struct conncb *c); -static void do_shutdown(void); - -static void listen_callback(void *, struct kevent *); static void async_callback(void); static void signal_callback(void *, struct kevent *); static void fs_callback(void); -static void simple_zombie_reaper(void *, struct kevent *); -static void readcfg_callback(void *, struct kevent *); +static void ppidexit_callback(void); +static void pfsystem_callback(void *, struct kevent *); -static kq_callback kqlisten_callback = listen_callback; static kq_callback kqasync_callback = (kq_callback)async_callback; static kq_callback kqsignal_callback = signal_callback; static kq_callback kqfs_callback = (kq_callback)fs_callback; -static kq_callback kqreadcfg_callback = readcfg_callback; -kq_callback kqsimple_zombie_reaper = simple_zombie_reaper; - -static void job_watch(struct jobcb *j); -static void job_ignore(struct jobcb *j); -static void job_start(struct jobcb *j); -static void job_start_child(struct jobcb *j, int execfd); -static void job_setup_attributes(struct jobcb *j); -static void job_stop(struct jobcb *j); -static void job_reap(struct jobcb *j); -static void job_remove(struct jobcb *j); -static void job_set_alarm(struct jobcb *j); -static void job_callback(void *obj, struct kevent *kev); -static void job_log(struct jobcb *j, int pri, const char *msg, ...) __attribute__((format(printf, 3, 4))); -static void job_log_error(struct jobcb *j, int pri, const char *msg, ...) __attribute__((format(printf, 3, 4))); -static void job_prep_log_msg(struct jobcb *j, char *buf, const char *msg, int err); - -static void ipc_open(int fd, struct jobcb *j); -static void ipc_close(struct conncb *c); -static void ipc_callback(void *, struct kevent *); -static void ipc_readmsg(launch_data_t msg, void *context); -static void ipc_readmsg2(launch_data_t data, const char *cmd, void *context); - -#ifdef PID1_REAP_ADOPTED_CHILDREN -static void pid1waitpid(void); -static bool launchd_check_pid(pid_t p); -#endif -static void pid1_magic_init(bool sflag, bool vflag, bool xflag); -static void launchd_server_init(bool create_session); -static void conceive_firstborn(char *argv[]); +static kq_callback kqppidexit_callback = (kq_callback)ppidexit_callback; +static kq_callback kqpfsystem_callback = pfsystem_callback; + +static void pid1_magic_init(bool sflag); static void usage(FILE *where); -static int _fd(int fd); -static void loopback_setup(void); -static void workaround3048875(int argc, char *argv[]); -static void reload_launchd_config(void); -static int dir_has_files(const char *path); static void testfd_or_openfd(int fd, const char *path, int flags); -static void setup_job_env(launch_data_t obj, const char *key, void *context); -static void unsetup_job_env(launch_data_t obj, const char *key, void *context); - -static time_t cronemu(int mon, int mday, int hour, int min); -static time_t cronemu_wday(int wday, int hour, int min); -static bool cronemu_mon(struct tm *wtm, int mon, int mday, int hour, int min); -static bool cronemu_mday(struct tm *wtm, int mday, int hour, int min); -static bool cronemu_hour(struct tm *wtm, int hour, int min); -static bool cronemu_min(struct tm *wtm, int min); - -static size_t total_children = 0; -static pid_t readcfg_pid = 0; -static pid_t launchd_proper_pid = 0; -static bool launchd_inited = false; -static bool shutdown_in_progress = false; -static pthread_t mach_server_loop_thread; -mach_port_t launchd_bootstrap_port = MACH_PORT_NULL; -sigset_t blocked_signals = 0; -pthread_mutex_t giant_lock = PTHREAD_MUTEX_INITIALIZER; +static bool get_network_state(void); +static void monitor_networking_state(void); +static void *kqueue_demand_loop(void *arg); + +static pthread_t kqueue_demand_thread; +static int mainkq = 0; +static int asynckq = 0; +static bool re_exec_in_single_user_mode = false; static char *pending_stdout = NULL; static char *pending_stderr = NULL; +static struct jobcb *rlcj = NULL; + +sigset_t blocked_signals = 0; +bool shutdown_in_progress = false; +bool network_up = false; +int batch_disabler_count = 0; +mach_port_t launchd_internal_port = MACH_PORT_NULL; +mach_port_t ipc_port_set = MACH_PORT_NULL; -int main(int argc, char *argv[]) +int +main(int argc, char *const *argv) { static const int sigigns[] = { SIGHUP, SIGINT, SIGPIPE, SIGALRM, SIGTERM, SIGURG, SIGTSTP, SIGTSTP, SIGCONT, /*SIGCHLD,*/ SIGTTIN, SIGTTOU, SIGIO, SIGXCPU, SIGXFSZ, SIGVTALRM, SIGPROF, - SIGWINCH, SIGINFO, SIGUSR1, SIGUSR2 }; - struct kevent kev; - size_t i; - bool sflag = false, xflag = false, vflag = false, dflag = false; - int ch; + SIGWINCH, SIGINFO, SIGUSR1, SIGUSR2 + }; + bool sflag = false, dflag = false, Dflag = false; + mach_msg_type_number_t l2l_name_cnt = 0, l2l_port_cnt = 0; + name_array_t l2l_names = NULL; + mach_port_array_t l2l_ports = NULL; + char ldconf[PATH_MAX] = PID1LAUNCHD_CONF; + const char *h = getenv("HOME"); + const char *session_type = NULL; + const char *optargs = NULL; + launch_data_t ldresp, ldmsg = launch_data_new_string(LAUNCH_KEY_CHECKIN); + struct jobcb *fbj = NULL; + struct stat sb; + size_t i, checkin_fdcnt = 0; + int *checkin_fds = NULL; + mach_port_t req_mport = MACH_PORT_NULL; + mach_port_t checkin_mport = MACH_PORT_NULL; + int ch, ker, logopts; + + /* main() phase one: sanitize the process */ + + if (getpid() != 1 && (ldresp = launch_msg(ldmsg)) && launch_data_get_type(ldresp) == LAUNCH_DATA_DICTIONARY) { + const char *ldlabel = launch_data_get_string(launch_data_dict_lookup(ldresp, LAUNCH_JOBKEY_LABEL)); + launch_data_t tmp; + + if ((tmp = launch_data_dict_lookup(ldresp, LAUNCH_JOBKEY_SOCKETS))) { + if ((tmp = launch_data_dict_lookup(tmp, "LaunchIPC"))) { + checkin_fdcnt = launch_data_array_get_count(tmp); + checkin_fds = alloca(sizeof(int) * checkin_fdcnt); + for (i = 0; i < checkin_fdcnt; i++) { + checkin_fds[i] = _fd(launch_data_get_fd(launch_data_array_get_index(tmp, i))); + } + } + } + if ((tmp = launch_data_dict_lookup(ldresp, LAUNCH_JOBKEY_MACHSERVICES))) { + if ((tmp = launch_data_dict_lookup(tmp, ldlabel))) { + checkin_mport = launch_data_get_machport(tmp); + } + } + launch_data_free(ldresp); + } else { + int sigi, fdi, dts = getdtablesize(); + sigset_t emptyset; + + for (fdi = STDERR_FILENO + 1; fdi < dts; fdi++) + close(fdi); + for (sigi = 1; sigi < NSIG; sigi++) + launchd_assumes(signal(sigi, SIG_DFL) != SIG_ERR); + sigemptyset(&emptyset); + launchd_assumes(sigprocmask(SIG_SETMASK, &emptyset, NULL) == 0); + } - __log_liblaunch_bug = _log_launchd_bug; + launch_data_free(ldmsg); - if (getpid() == 1) - workaround3048875(argc, argv); + testfd_or_openfd(STDIN_FILENO, _PATH_DEVNULL, O_RDONLY|O_NOCTTY); + testfd_or_openfd(STDOUT_FILENO, _PATH_DEVNULL, O_WRONLY|O_NOCTTY); + testfd_or_openfd(STDERR_FILENO, _PATH_DEVNULL, O_WRONLY|O_NOCTTY); - setegid(getgid()); - seteuid(getuid()); + /* main phase two: parse arguments */ - testfd_or_openfd(STDIN_FILENO, _PATH_DEVNULL, O_RDONLY); - testfd_or_openfd(STDOUT_FILENO, _PATH_DEVNULL, O_WRONLY); - testfd_or_openfd(STDERR_FILENO, _PATH_DEVNULL, O_WRONLY); + if (getpid() == 1) { + optargs = "s"; + } else { + optargs = "DS:dh"; + } - openlog(getprogname(), LOG_CONS|(getpid() != 1 ? LOG_PID|LOG_PERROR : 0), LOG_LAUNCHD); - setlogmask(LOG_UPTO(LOG_NOTICE)); - - while ((ch = getopt(argc, argv, "dhsvx")) != -1) { + while ((ch = getopt(argc, argv, optargs)) != -1) { switch (ch) { - case 'd': dflag = true; break; - case 's': sflag = true; break; - case 'x': xflag = true; break; - case 'v': vflag = true; break; - case 'h': usage(stdout); break; + case 'S': session_type = optarg; break; /* what type of session we're creating */ + case 'D': Dflag = true; break; /* debug */ + case 'd': dflag = true; break; /* daemonize */ + case 's': sflag = true; break; /* single user */ + case 'h': usage(stdout); break; /* help */ + case '?': /* we should do something with the global optopt variable here */ default: - syslog(LOG_WARNING, "ignoring unknown arguments"); + fprintf(stderr, "ignoring unknown arguments\n"); usage(stderr); break; } @@ -227,2189 +217,530 @@ int main(int argc, char *argv[]) argc -= optind; argv += optind; - if (dflag) { - assumes(daemon(0, 0) != -1); - } + /* main phase three: get the party started */ - if (!assumes((mainkq = kqueue()) != -1)) { - abort(); - } + if (dflag) + launchd_assumes(daemon(0, 0) == 0); - if (!assumes((asynckq = kqueue()) != -1)) { - abort(); - } + launchd_assert((errno = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_PORT_SET, &ipc_port_set)) == KERN_SUCCESS); + launchd_assert(launchd_mport_create_recv(&launchd_internal_port) == KERN_SUCCESS); + launchd_assert(launchd_mport_make_send(launchd_internal_port) == KERN_SUCCESS); + launchd_assert((errno = mach_port_move_member(mach_task_self(), launchd_internal_port, ipc_port_set)) == KERN_SUCCESS); + + logopts = LOG_PID|LOG_CONS; + if (Dflag) + logopts |= LOG_PERROR; + + openlog(getprogname(), logopts, LOG_LAUNCHD); + setlogmask(LOG_UPTO(Dflag ? LOG_DEBUG : LOG_NOTICE)); + + launchd_assert((mainkq = kqueue()) != -1); + + launchd_assert((asynckq = kqueue()) != -1); - if (!assumes(kevent_mod(asynckq, EVFILT_READ, EV_ADD, 0, 0, &kqasync_callback) != -1)) { - abort(); - } + launchd_assert(kevent_mod(asynckq, EVFILT_READ, EV_ADD, 0, 0, &kqasync_callback) != -1); sigemptyset(&blocked_signals); for (i = 0; i < (sizeof(sigigns) / sizeof(int)); i++) { - assumes(kevent_mod(sigigns[i], EVFILT_SIGNAL, EV_ADD, 0, 0, &kqsignal_callback) != -1); + launchd_assumes(kevent_mod(sigigns[i], EVFILT_SIGNAL, EV_ADD, 0, 0, &kqsignal_callback) != -1); sigaddset(&blocked_signals, sigigns[i]); - signal(sigigns[i], SIG_IGN); + launchd_assumes(signal(sigigns[i], SIG_IGN) != SIG_ERR); } /* sigh... ignoring SIGCHLD has side effects: we can't call wait*() */ - assumes(kevent_mod(SIGCHLD, EVFILT_SIGNAL, EV_ADD, 0, 0, &kqsignal_callback) != -1); - - assumes(pthread_mutex_lock(&giant_lock) == 0); - - if (getpid() == 1) { - pid1_magic_init(sflag, vflag, xflag); - } else { - launchd_bootstrap_port = bootstrap_port; - launchd_server_init(argv[0] ? true : false); - } + launchd_assert(kevent_mod(SIGCHLD, EVFILT_SIGNAL, EV_ADD, 0, 0, &kqsignal_callback) != -1); - /* do this after pid1_magic_init() to not catch ourselves mounting stuff */ - assumes(kevent_mod(0, EVFILT_FS, EV_ADD, 0, 0, &kqfs_callback) != -1); + if (session_type && strcmp(session_type, "Aqua") == 0) { + mach_port_t newparent; + launchd_assert(bootstrap_parent(bootstrap_port, &newparent) == BOOTSTRAP_SUCCESS); - if (argv[0]) - conceive_firstborn(argv); + launchd_assert(_launchd_to_launchd(bootstrap_port, &req_mport, &checkin_mport, + &l2l_names, &l2l_name_cnt, &l2l_ports, &l2l_port_cnt) == BOOTSTRAP_SUCCESS); - reload_launchd_config(); + launchd_assert(l2l_name_cnt == l2l_port_cnt); - if (argv[0]) - job_start(TAILQ_FIRST(&jobs)); + task_set_bootstrap_port(mach_task_self(), newparent); + launchd_assumes(mach_port_deallocate(mach_task_self(), bootstrap_port) == KERN_SUCCESS); + bootstrap_port = newparent; + } - for (;;) { - static struct timespec timeout = { 30, 0 }; - struct timespec *timeoutp = NULL; - int kev_r; + mach_init_init(req_mport, checkin_mport, l2l_names, l2l_ports, l2l_name_cnt); - if (getpid() == 1) { - if (readcfg_pid == 0) - init_pre_kevent(); - } else { - if (TAILQ_EMPTY(&jobs)) { - /* launched on demand */ - timeoutp = &timeout; - } else if (shutdown_in_progress && total_children == 0) { - exit(EXIT_SUCCESS); - } - } + if (h) + sprintf(ldconf, "%s/%s", h, LAUNCHD_CONF); - assumes(pthread_mutex_unlock(&giant_lock) == 0); - kev_r = kevent(mainkq, NULL, 0, &kev, 1, timeoutp); - assumes(pthread_mutex_lock(&giant_lock) == 0); + rlcj = job_new(root_job, READCONF_LABEL, LAUNCHCTL_PATH, NULL, ldconf, MACH_PORT_NULL); + launchd_assert(rlcj != NULL); - switch (kev_r) { - case -1: - syslog(LOG_DEBUG, "kevent(): %m"); - break; - case 1: - (*((kq_callback *)kev.udata))(kev.udata, &kev); - break; - case 0: - if (timeoutp) - exit(EXIT_SUCCESS); - else - syslog(LOG_DEBUG, "kevent(): spurious return with infinite timeout"); - break; - default: - syslog(LOG_DEBUG, "unexpected: kevent() returned something != 0, -1 or 1"); - break; - } - } -} + if (argv[0]) + fbj = job_new(root_job, FIRSTBORN_LABEL, NULL, (const char *const *)argv, NULL, MACH_PORT_NULL); -static void pid1_magic_init(bool sflag, bool vflag, bool xflag) -{ - pthread_attr_t attr; - int memmib[2] = { CTL_HW, HW_MEMSIZE }; - int mvnmib[2] = { CTL_KERN, KERN_MAXVNODES }; - int hnmib[2] = { CTL_KERN, KERN_HOSTNAME }; -#ifdef KERN_TFP - int tfp_r_mib[3] = { CTL_KERN, KERN_TFP, KERN_TFP_READ_GROUP }; - int tfp_rw_mib[3] = { CTL_KERN, KERN_TFP, KERN_TFP_RW_GROUP }; - gid_t tfp_r_gid = 0; - gid_t tfp_rw_gid = 0; - struct group *tfp_gr; -#endif - uint64_t mem = 0; - uint32_t mvn; - size_t memsz = sizeof(mem); - -#ifdef KERN_TFP - if ((tfp_gr = getgrnam("procview"))) { - tfp_r_gid = tfp_gr->gr_gid; - assumes(sysctl(tfp_r_mib, 3, NULL, NULL, &tfp_r_gid, sizeof(tfp_r_gid)) != -1); - } + if (NULL == getenv("PATH")) + setenv("PATH", _PATH_STDPATH, 1); - if ((tfp_gr = getgrnam("procmod"))) { - tfp_rw_gid = tfp_gr->gr_gid; - assumes(sysctl(tfp_rw_mib, 3, NULL, NULL, &tfp_rw_gid, sizeof(tfp_rw_gid)) != -1); + if (getpid() == 1) { + pid1_magic_init(sflag); + } else { + ipc_server_init(checkin_fds, checkin_fdcnt); } -#endif - setpriority(PRIO_PROCESS, 0, -1); + monitor_networking_state(); - assumes(setsid() != -1); + /* do this after pid1_magic_init() to not catch ourselves mounting stuff */ + launchd_assumes(kevent_mod(0, EVFILT_FS, EV_ADD, 0, 0, &kqfs_callback) != -1); - assumes(chdir("/") != -1); + if (session_type) { + pid_t pp = getppid(); - if (assumes(sysctl(memmib, 2, &mem, &memsz, NULL, 0) != -1)) { - /* The following assignment of mem to itself if the size - * of data returned is 32 bits instead of 64 is a clever - * C trick to move the 32 bits on big endian systems to - * the least significant bytes of the 64 mem variable. + /* As a per session launchd, we need to exit if our parent dies. * - * On little endian systems, this is effectively a no-op. + * Normally, in Unix, SIGHUP would cause us to exit, but we're a + * daemon, and daemons use SIGHUP to signal the need to reread + * configuration files. "Weee." */ - if (memsz == 4) - mem = *(uint32_t *)&mem; - mvn = mem / (64 * 1024) + 1024; - assumes(sysctl(mvnmib, 2, NULL, NULL, &mvn, sizeof(mvn)) != -1); - } - assumes(sysctl(hnmib, 2, NULL, NULL, "localhost", sizeof("localhost")) != -1); - - assumes(setlogin("root") != -1); - loopback_setup(); - - assumes(mount("fdesc", "/dev", MNT_UNION, NULL) != -1); - - setenv("PATH", _PATH_STDPATH, 1); - - launchd_bootstrap_port = mach_init_init(); - assumes(task_set_bootstrap_port(mach_task_self(), launchd_bootstrap_port) == KERN_SUCCESS); - bootstrap_port = MACH_PORT_NULL; + if (pp == 1) + exit(EXIT_SUCCESS); - assumes(pthread_attr_init(&attr) == 0); - assumes(pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) == 0); + ker = kevent_mod(pp, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, &kqppidexit_callback); - if (!assumes(pthread_create(&mach_server_loop_thread, &attr, mach_server_loop, NULL) == 0)) { - abort(); + if (ker == -1) + exit(launchd_assumes(errno == ESRCH) ? EXIT_SUCCESS : EXIT_FAILURE); } - pthread_attr_destroy(&attr); + if (stat(ldconf, &sb) == 0) + job_start(rlcj); - init_boot(sflag, vflag, xflag); -} + if (fbj) + job_start(fbj); + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN); + launchd_assert(pthread_create(&kqueue_demand_thread, &attr, kqueue_demand_loop, NULL) == 0); + pthread_attr_destroy(&attr); -#ifdef PID1_REAP_ADOPTED_CHILDREN -static bool launchd_check_pid(pid_t p) -{ - struct kevent kev; - struct jobcb *j; + mach_msg_return_t msgr; + mach_msg_size_t mxmsgsz = sizeof(union MaxRequestSize) + MAX_TRAILER_SIZE; - TAILQ_FOREACH(j, &jobs, tqe) { - if (j->p == p) { - EV_SET(&kev, p, EVFILT_PROC, 0, 0, 0, j); - j->kqjob_callback(j, &kev); - return true; - } - } + if (getpid() == 1 && !job_active(rlcj)) + init_pre_kevent(); - if (p == readcfg_pid) { - readcfg_callback(NULL, NULL); - return true; + for (;;) { + msgr = mach_msg_server(launchd_internal_demux, mxmsgsz, ipc_port_set, + MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT) | + MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0)); + launchd_assumes(msgr == MACH_MSG_SUCCESS); } - - return false; } -#endif -static char *sockdir = NULL; -static char *sockpath = NULL; - -static void launchd_clean_up(void) +void * +kqueue_demand_loop(void *arg __attribute__((unused))) { - if (launchd_proper_pid != getpid()) - return; + fd_set rfds; - seteuid(0); - setegid(0); - - if (assumes(unlink(sockpath) != -1)) - assumes(rmdir(sockdir) != -1); + for (;;) { + FD_ZERO(&rfds); + FD_SET(mainkq, &rfds); + if (launchd_assumes(select(mainkq + 1, &rfds, NULL, NULL, NULL) == 1)) + launchd_assumes(handle_kqueue(launchd_internal_port, mainkq) == 0); + } - setegid(getgid()); - seteuid(getuid()); + return NULL; } -static void launchd_server_init(bool create_session) +kern_return_t +x_handle_kqueue(mach_port_t junk __attribute__((unused)), integer_t fd) { - struct sockaddr_un sun; - mode_t oldmask; - int r, fd = -1, ourdirfd = -1; - char ourdir[1024]; - - memset(&sun, 0, sizeof(sun)); - sun.sun_family = AF_UNIX; - - if (create_session) { - snprintf(ourdir, sizeof(ourdir), "%s/%u.%u", LAUNCHD_SOCK_PREFIX, getuid(), getpid()); - snprintf(sun.sun_path, sizeof(sun.sun_path), "%s/%u.%u/sock", LAUNCHD_SOCK_PREFIX, getuid(), getpid()); - setenv(LAUNCHD_SOCKET_ENV, sun.sun_path, 1); - } else { - snprintf(ourdir, sizeof(ourdir), "%s/%u", LAUNCHD_SOCK_PREFIX, getuid()); - snprintf(sun.sun_path, sizeof(sun.sun_path), "%s/%u/sock", LAUNCHD_SOCK_PREFIX, getuid()); - } - - seteuid(0); - setegid(0); - - if (mkdir(LAUNCHD_SOCK_PREFIX, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) == -1) { - if (errno == EROFS) { - goto out_bad; - } else if (errno == EEXIST) { - struct stat sb; - stat(LAUNCHD_SOCK_PREFIX, &sb); - if (!S_ISDIR(sb.st_mode)) { - errno = EEXIST; - syslog(LOG_ERR, "mkdir(\"%s\"): %m", LAUNCHD_SOCK_PREFIX); - goto out_bad; - } - } else { - syslog(LOG_ERR, "mkdir(\"%s\"): %m", LAUNCHD_SOCK_PREFIX); - goto out_bad; - } - } + struct timespec ts = { 0, 0 }; + struct kevent kev; + int kevr; - unlink(ourdir); - if (mkdir(ourdir, S_IRWXU) == -1) { - if (errno == EROFS) { - goto out_bad; - } else if (errno == EEXIST) { - struct stat sb; - stat(ourdir, &sb); - if (!S_ISDIR(sb.st_mode)) { - errno = EEXIST; - syslog(LOG_ERR, "mkdir(\"%s\"): %m", LAUNCHD_SOCK_PREFIX); - goto out_bad; - } - } else { - syslog(LOG_ERR, "mkdir(\"%s\"): %m", ourdir); - goto out_bad; - } - } + launchd_assumes((kevr = kevent(fd, NULL, 0, &kev, 1, &ts)) != -1); - assumes(chown(ourdir, getuid(), getgid()) != -1); + if (kevr == 1) + (*((kq_callback *)kev.udata))(kev.udata, &kev); - setegid(getgid()); - seteuid(getuid()); + if (shutdown_in_progress && total_children == 0) { + mach_init_reap(); - if (!assumes((ourdirfd = _fd(open(ourdir, O_RDONLY))) != -1)) { - goto out_bad; - } + shutdown_in_progress = false; - if (flock(ourdirfd, LOCK_EX|LOCK_NB) == -1) { - if (errno == EWOULDBLOCK) { + if (getpid() != 1) { exit(EXIT_SUCCESS); - } else { - syslog(LOG_ERR, "flock(\"%s\"): %m", ourdir); - goto out_bad; + } else if (re_exec_in_single_user_mode) { + re_exec_in_single_user_mode = false; + launchd_assumes(execl("/sbin/launchd", "/sbin/launchd", "-s", NULL) != -1); } } - if (unlink(sun.sun_path) == -1 && errno != ENOENT) { - if (errno != EROFS) - syslog(LOG_ERR, "unlink(\"thesocket\"): %m"); - goto out_bad; - } - if (!assumes((fd = _fd(socket(AF_UNIX, SOCK_STREAM, 0))) != -1)) { - goto out_bad; - } - oldmask = umask(077); - r = bind(fd, (struct sockaddr *)&sun, sizeof(sun)); - umask(oldmask); - if (r == -1) { - if (errno != EROFS) - syslog(LOG_ERR, "bind(\"thesocket\"): %m"); - goto out_bad; - } - - if (!assumes(listen(fd, SOMAXCONN) != -1)) { - goto out_bad; + if (getpid() == 1) { + if (rlcj && job_active(rlcj)) + goto out; + init_pre_kevent(); } - if (!assumes(kevent_mod(fd, EVFILT_READ, EV_ADD, 0, 0, &kqlisten_callback) != -1)) { - goto out_bad; - } +out: + return 0; +} - launchd_inited = true; +void +pid1_magic_init(bool sflag) +{ + launchd_assumes(setsid() != -1); + launchd_assumes(chdir("/") != -1); + launchd_assumes(setlogin("root") != -1); + launchd_assumes(mount("fdesc", "/dev", MNT_UNION, NULL) != -1); - sockdir = strdup(ourdir); - sockpath = strdup(sun.sun_path); + init_boot(sflag); +} - launchd_proper_pid = getpid(); - atexit(launchd_clean_up); -out_bad: - setegid(getgid()); - seteuid(getuid()); +void +usage(FILE *where) +{ + const char *opts = "[-d]"; - if (!launchd_inited) { - if (fd != -1) - assumes(close(fd) != -1); - if (ourdirfd != -1) - assumes(close(ourdirfd) != -1); - } -} + if (getuid() == 0) + opts = "[-d] [-S -U ]"; -static long long job_get_integer(launch_data_t j, const char *key) -{ - launch_data_t t; + fprintf(where, "%s: %s [-- command [args ...]]\n", getprogname(), opts); - if (!assumes(j != NULL)) - return -1; + fprintf(where, "\t-d Daemonize.\n"); + fprintf(where, "\t-h This usage statement.\n"); - if ((t = launch_data_dict_lookup(j, key))) { - return launch_data_get_integer(t); - } else { - return 0; + if (getuid() == 0) { + fprintf(where, "\t-S What type of session to create (Aqua, tty or X11).\n"); + fprintf(where, "\t-U Which user to create the session as.\n"); } + + if (where == stdout) + exit(EXIT_SUCCESS); } -static const char *job_get_string(launch_data_t j, const char *key) +int +kevent_mod(uintptr_t ident, short filter, u_short flags, u_int fflags, intptr_t data, void *udata) { - launch_data_t t; + struct kevent kev; + int q = mainkq; - if (!assumes(j != NULL)) - return NULL; + if (EVFILT_TIMER == filter || EVFILT_VNODE == filter) + q = asynckq; - if ((t = launch_data_dict_lookup(j, key))) { - return launch_data_get_string(t); - } else { - return NULL; + if (flags & EV_ADD && !launchd_assumes(udata != NULL)) { + errno = EINVAL; + return -1; } + + EV_SET(&kev, ident, filter, flags, fflags, data, udata); + return kevent(q, &kev, 1, NULL, 0, NULL); } -static const char *job_get_file2exec(launch_data_t j) +int +_fd(int fd) { - launch_data_t tmpi, tmp; + if (fd >= 0) + launchd_assumes(fcntl(fd, F_SETFD, 1) != -1); + return fd; +} - if (!assumes(j != NULL)) - return NULL; +void +ppidexit_callback(void) +{ + launchd_shutdown(); - if ((tmp = launch_data_dict_lookup(j, LAUNCH_JOBKEY_PROGRAM))) { - return launch_data_get_string(tmp); - } else { - tmp = launch_data_dict_lookup(j, LAUNCH_JOBKEY_PROGRAMARGUMENTS); - if (assumes(tmp != NULL)) { - tmpi = launch_data_array_get_index(tmp, 0); - if (assumes(tmpi != NULL)) - return launch_data_get_string(tmpi); - } - return NULL; - } + /* Let's just bail for now. We should really try to wait for jobs to exit first. */ + exit(EXIT_SUCCESS); } -static bool job_get_bool(launch_data_t j, const char *key) +void +launchd_shutdown(void) { - launch_data_t t; + shutdown_in_progress = true; + + launchd_assumes(close(asynckq) != -1); + + rlcj = NULL; - if (!assumes(j != NULL)) - return false; + job_remove_all_inactive(root_job); - if ((t = launch_data_dict_lookup(j, key))) { - return launch_data_get_bool(t); - } else { - return false; - } + if (getpid() == 1) + catatonia(); } -static void ipc_open(int fd, struct jobcb *j) +void +launchd_single_user(void) { - struct conncb *c = calloc(1, sizeof(struct conncb)); + int tries; - if (!assumes(c != NULL)) - return; + launchd_shutdown(); - assumes(fcntl(fd, F_SETFL, O_NONBLOCK) != -1); + kill(-1, SIGTERM); - c->kqconn_callback = ipc_callback; - c->conn = launchd_fdopen(fd); - c->j = j; - TAILQ_INSERT_TAIL(&connections, c, tqe); - assumes(kevent_mod(fd, EVFILT_READ, EV_ADD, 0, 0, &c->kqconn_callback) != -1); -} + for (tries = 0; tries < 10; tries++) { + sleep(1); + if (kill(-1, 0) == -1 && errno == ESRCH) + goto out; + } -static void simple_zombie_reaper(void *obj __attribute__((unused)), struct kevent *kev) -{ - assumes(waitpid(kev->ident, NULL, 0) != -1); + syslog(LOG_WARNING, "Gave up waiting for processes to exit while going to single user mode, sending SIGKILL"); + kill(-1, SIGKILL); + +out: + re_exec_in_single_user_mode = true; } -static void listen_callback(void *obj __attribute__((unused)), struct kevent *kev) +static void signal_callback(void *obj __attribute__((unused)), struct kevent *kev) { - struct sockaddr_un sun; - socklen_t sl = sizeof(sun); - int cfd; - - if (assumes((cfd = _fd(accept(kev->ident, (struct sockaddr *)&sun, &sl))) != -1)) { - ipc_open(cfd, NULL); - } + switch (kev->ident) { + case SIGHUP: + if (rlcj) + job_start(rlcj); + break; + case SIGTERM: + launchd_shutdown(); + break; + default: + break; + } } -static void ipc_callback(void *obj, struct kevent *kev) +void +fs_callback(void) { - struct conncb *c = obj; - int r; - - if (kev->filter == EVFILT_READ) { - if (launchd_msg_recv(c->conn, ipc_readmsg, c) == -1 && errno != EAGAIN) { - if (errno != ECONNRESET) - syslog(LOG_DEBUG, "%s(): recv: %m", __func__); - ipc_close(c); + if (pending_stdout) { + int fd = open(pending_stdout, O_CREAT|O_APPEND|O_WRONLY|O_NOCTTY, DEFFILEMODE); + if (fd != -1) { + launchd_assumes(dup2(fd, STDOUT_FILENO) != -1); + launchd_assumes(close(fd) == 0); + free(pending_stdout); + pending_stdout = NULL; } - } else if (kev->filter == EVFILT_WRITE) { - r = launchd_msg_send(c->conn, NULL); - if (r == -1) { - if (errno != EAGAIN) { - syslog(LOG_DEBUG, "%s(): send: %m", __func__); - ipc_close(c); - } - } else if (r == 0) { - assumes(kevent_mod(launchd_getfd(c->conn), EVFILT_WRITE, EV_DELETE, 0, 0, NULL) != -1); + } + if (pending_stderr) { + int fd = open(pending_stderr, O_CREAT|O_APPEND|O_WRONLY|O_NOCTTY, DEFFILEMODE); + if (fd != -1) { + launchd_assumes(dup2(fd, STDERR_FILENO) != -1); + launchd_assumes(close(fd) == 0); + free(pending_stderr); + pending_stderr = NULL; } - } else { - syslog(LOG_DEBUG, "%s(): unknown filter type!", __func__); - ipc_close(c); } -} -static void set_user_env(launch_data_t obj, const char *key, void *context __attribute__((unused))) -{ - setenv(key, launch_data_get_string(obj), 1); + ipc_server_init(NULL, 0); } -static void launch_data_close_fds(launch_data_t o) +void +launchd_SessionCreate(void) { - size_t i; + OSStatus (*sescr)(SessionCreationFlags flags, SessionAttributeBits attributes); + void *seclib; - switch (launch_data_get_type(o)) { - case LAUNCH_DATA_DICTIONARY: - launch_data_dict_iterate(o, (void (*)(launch_data_t, const char *, void *))launch_data_close_fds, NULL); - break; - case LAUNCH_DATA_ARRAY: - for (i = 0; i < launch_data_array_get_count(o); i++) - launch_data_close_fds(launch_data_array_get_index(o, i)); - break; - case LAUNCH_DATA_FD: - if (launch_data_get_fd(o) != -1) - assumes(close(launch_data_get_fd(o)) != -1); - break; - default: - break; + if (launchd_assumes((seclib = dlopen(SECURITY_LIB, RTLD_LAZY)) != NULL)) { + if (launchd_assumes((sescr = dlsym(seclib, "SessionCreate")) != NULL)) + launchd_assumes(sescr(0, 0) == noErr); + launchd_assumes(dlclose(seclib) != -1); } } -static void launch_data_revoke_fds(launch_data_t o) +void +async_callback(void) { - size_t i; + struct timespec timeout = { 0, 0 }; + struct kevent kev; - switch (launch_data_get_type(o)) { - case LAUNCH_DATA_DICTIONARY: - launch_data_dict_iterate(o, (void (*)(launch_data_t, const char *, void *))launch_data_revoke_fds, NULL); - break; - case LAUNCH_DATA_ARRAY: - for (i = 0; i < launch_data_array_get_count(o); i++) - launch_data_revoke_fds(launch_data_array_get_index(o, i)); - break; - case LAUNCH_DATA_FD: - launch_data_set_fd(o, -1); - break; - default: - break; - } + if (launchd_assumes(kevent(asynckq, NULL, 0, &kev, 1, &timeout) == 1)) + (*((kq_callback *)kev.udata))(kev.udata, &kev); } -static void job_ignore_fds(launch_data_t o, const char *key __attribute__((unused)), void *cookie) +void +testfd_or_openfd(int fd, const char *path, int flags) { - struct jobcb *j = cookie; - size_t i; - int fd; + int tmpfd; - switch (launch_data_get_type(o)) { - case LAUNCH_DATA_DICTIONARY: - launch_data_dict_iterate(o, job_ignore_fds, cookie); - break; - case LAUNCH_DATA_ARRAY: - for (i = 0; i < launch_data_array_get_count(o); i++) - job_ignore_fds(launch_data_array_get_index(o, i), NULL, cookie); - break; - case LAUNCH_DATA_FD: - fd = launch_data_get_fd(o); - if (-1 != fd) { - job_log(j, LOG_DEBUG, "Ignoring FD: %d", fd); - assumes(kevent_mod(fd, EVFILT_READ, EV_DELETE, 0, 0, NULL) != -1); + if (-1 != (tmpfd = dup(fd))) { + launchd_assumes(close(tmpfd) == 0); + } else { + if (-1 == (tmpfd = open(path, flags))) { + syslog(LOG_ERR, "open(\"%s\", ...): %m", path); + } else if (tmpfd != fd) { + launchd_assumes(dup2(tmpfd, fd) != -1); + launchd_assumes(close(tmpfd) == 0); } - break; - default: - break; } } -static void job_ignore(struct jobcb *j) +launch_data_t +launchd_setstdio(int d, launch_data_t o) { - launch_data_t j_sockets = launch_data_dict_lookup(j->ldj, LAUNCH_JOBKEY_SOCKETS); - size_t i; + launch_data_t resp = launch_data_new_errno(0); - if (j_sockets) - job_ignore_fds(j_sockets, NULL, j); + if (launch_data_get_type(o) == LAUNCH_DATA_STRING) { + char **where = &pending_stderr; - for (i = 0; i < j->vnodes_cnt; i++) { - assumes(kevent_mod(j->vnodes[i], EVFILT_VNODE, EV_DELETE, 0, 0, NULL) != -1); - } - for (i = 0; i < j->qdirs_cnt; i++) { - assumes(kevent_mod(j->qdirs[i], EVFILT_VNODE, EV_DELETE, 0, 0, NULL) != -1); + if (d == STDOUT_FILENO) + where = &pending_stdout; + if (*where) + free(*where); + *where = strdup(launch_data_get_string(o)); + } else if (launch_data_get_type(o) == LAUNCH_DATA_FD) { + launchd_assumes(dup2(launch_data_get_fd(o), d) != -1); + } else { + launch_data_set_errno(resp, EINVAL); } + + return resp; } -static void job_watch_fds(launch_data_t o, const char *key __attribute__((unused)), void *cookie) +void +batch_job_enable(bool e, struct conncb *c) { - struct jobcb *j = cookie; - size_t i; - int fd; + if (e && c->disabled_batch) { + batch_disabler_count--; + c->disabled_batch = 0; + if (batch_disabler_count == 0) + kevent_mod(asynckq, EVFILT_READ, EV_ENABLE, 0, 0, &kqasync_callback); + } else if (!e && !c->disabled_batch) { + if (batch_disabler_count == 0) + kevent_mod(asynckq, EVFILT_READ, EV_DISABLE, 0, 0, &kqasync_callback); + batch_disabler_count++; + c->disabled_batch = 1; + } +} - switch (launch_data_get_type(o)) { - case LAUNCH_DATA_DICTIONARY: - launch_data_dict_iterate(o, job_watch_fds, cookie); - break; - case LAUNCH_DATA_ARRAY: - for (i = 0; i < launch_data_array_get_count(o); i++) - job_watch_fds(launch_data_array_get_index(o, i), NULL, cookie); - break; - case LAUNCH_DATA_FD: - fd = launch_data_get_fd(o); - if (-1 != fd) { - job_log(j, LOG_DEBUG, "Watching FD: %d", fd); - assumes(kevent_mod(fd, EVFILT_READ, EV_ADD, 0, 0, cookie) != -1); - } - break; - default: +bool +get_network_state(void) +{ + struct ifaddrs *ifa, *ifai; + bool up = false; + + if (!launchd_assumes(getifaddrs(&ifa) != -1)) + return network_up; + + for (ifai = ifa; ifai; ifai = ifai->ifa_next) { + if (!(ifai->ifa_flags & IFF_UP)) + continue; + if (ifai->ifa_flags & IFF_LOOPBACK) + continue; + if (ifai->ifa_addr->sa_family != AF_INET && ifai->ifa_addr->sa_family != AF_INET6) + continue; + up = true; break; } + + freeifaddrs(ifa); + + return up; } -static void job_watch(struct jobcb *j) +void +monitor_networking_state(void) { - launch_data_t ld_qdirs = launch_data_dict_lookup(j->ldj, LAUNCH_JOBKEY_QUEUEDIRECTORIES); - launch_data_t ld_vnodes = launch_data_dict_lookup(j->ldj, LAUNCH_JOBKEY_WATCHPATHS); - launch_data_t j_sockets = launch_data_dict_lookup(j->ldj, LAUNCH_JOBKEY_SOCKETS); - size_t i; + int pfs = _fd(socket(PF_SYSTEM, SOCK_RAW, SYSPROTO_EVENT)); + struct kev_request kev_req; - if (j_sockets) - job_watch_fds(j_sockets, NULL, &j->kqjob_callback); + network_up = get_network_state(); - for (i = 0; i < j->vnodes_cnt; i++) { - if (-1 == j->vnodes[i]) { - launch_data_t ld_idx = launch_data_array_get_index(ld_vnodes, i); - const char *thepath = launch_data_get_string(ld_idx); + if (!launchd_assumes(pfs != -1)) + return; - if (-1 == (j->vnodes[i] = _fd(open(thepath, O_EVTONLY)))) - job_log_error(j, LOG_ERR, "open(\"%s\", O_EVTONLY)", thepath); - } - assumes(kevent_mod(j->vnodes[i], EVFILT_VNODE, EV_ADD|EV_CLEAR, - NOTE_WRITE|NOTE_EXTEND|NOTE_DELETE|NOTE_RENAME|NOTE_REVOKE|NOTE_ATTRIB|NOTE_LINK, - 0, &j->kqjob_callback) != -1); - } + memset(&kev_req, 0, sizeof(kev_req)); + kev_req.vendor_code = KEV_VENDOR_APPLE; + kev_req.kev_class = KEV_NETWORK_CLASS; - for (i = 0; i < j->qdirs_cnt; i++) { - assumes(kevent_mod(j->qdirs[i], EVFILT_VNODE, EV_ADD|EV_CLEAR, - NOTE_WRITE|NOTE_EXTEND|NOTE_ATTRIB|NOTE_LINK, 0, &j->kqjob_callback) != -1); - } - - for (i = 0; i < j->qdirs_cnt; i++) { - launch_data_t ld_idx = launch_data_array_get_index(ld_qdirs, i); - const char *thepath = launch_data_get_string(ld_idx); - int dcc_r; - - if (-1 == (dcc_r = dir_has_files(thepath))) { - job_log_error(j, LOG_ERR, "dir_has_files(\"%s\", ...)", thepath); - } else if (dcc_r > 0 && !shutdown_in_progress) { - job_start(j); - break; - } - } -} - -static void job_stop(struct jobcb *j) -{ - if (j->p) - assumes(kill(j->p, SIGTERM) != -1); -} - -static void job_remove(struct jobcb *j) -{ - launch_data_t tmp; - size_t i; - - job_log(j, LOG_DEBUG, "Removed"); - - TAILQ_REMOVE(&jobs, j, tqe); - if (j->p) { - if (kevent_mod(j->p, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, &kqsimple_zombie_reaper) == -1) { - job_reap(j); - } else { - job_stop(j); - } - } - if ((tmp = launch_data_dict_lookup(j->ldj, LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES))) - launch_data_dict_iterate(tmp, unsetup_job_env, NULL); - launch_data_close_fds(j->ldj); - launch_data_free(j->ldj); - if (j->execfd) - close(j->execfd); - for (i = 0; i < j->vnodes_cnt; i++) - if (-1 != j->vnodes[i]) - close(j->vnodes[i]); - if (j->vnodes) - free(j->vnodes); - for (i = 0; i < j->qdirs_cnt; i++) - if (-1 != j->qdirs[i]) - close(j->qdirs[i]); - if (j->qdirs) - free(j->qdirs); - if (j->start_interval) - assumes(kevent_mod((uintptr_t)&j->start_interval, EVFILT_TIMER, EV_DELETE, 0, 0, NULL) != -1); - if (j->start_cal_interval) { - assumes(kevent_mod((uintptr_t)j->start_cal_interval, EVFILT_TIMER, EV_DELETE, 0, 0, NULL) != -1); - free(j->start_cal_interval); - } - kevent_mod((uintptr_t)j, EVFILT_TIMER, EV_DELETE, 0, 0, NULL); - free(j); -} - -struct readmsg_context { - struct conncb *c; - launch_data_t resp; -}; - -static void ipc_readmsg(launch_data_t msg, void *context) -{ - struct readmsg_context rmc = { context, NULL }; - - if (LAUNCH_DATA_DICTIONARY == launch_data_get_type(msg)) { - launch_data_dict_iterate(msg, ipc_readmsg2, &rmc); - } else if (LAUNCH_DATA_STRING == launch_data_get_type(msg)) { - ipc_readmsg2(NULL, launch_data_get_string(msg), &rmc); - } else { - rmc.resp = launch_data_new_errno(EINVAL); - } - - if (NULL == rmc.resp) - rmc.resp = launch_data_new_errno(ENOSYS); - - launch_data_close_fds(msg); - - if (launchd_msg_send(rmc.c->conn, rmc.resp) == -1) { - if (errno == EAGAIN) { - assumes(kevent_mod(launchd_getfd(rmc.c->conn), EVFILT_WRITE, EV_ADD, 0, 0, &rmc.c->kqconn_callback) != -1); - } else { - syslog(LOG_DEBUG, "launchd_msg_send() == -1: %m"); - ipc_close(rmc.c); - } - } - launch_data_free(rmc.resp); -} - -static void -attach_bonjourfds_to_job(launch_data_t o, const char *key, void *context __attribute__((unused))) -{ - struct jobcb *j = NULL; - - TAILQ_FOREACH(j, &jobs, tqe) { - if (strcmp(j->label, key) == 0) - break; - } - - if (j == NULL) - return; - - launch_data_dict_insert(j->ldj, launch_data_copy(o), LAUNCH_JOBKEY_BONJOURFDS); - launch_data_revoke_fds(o); -} - -static void ipc_readmsg2(launch_data_t data, const char *cmd, void *context) -{ - struct readmsg_context *rmc = context; - launch_data_t resp = NULL; - struct jobcb *j; - - if (rmc->resp) - return; - - if (!strcmp(cmd, LAUNCH_KEY_STARTJOB)) { - TAILQ_FOREACH(j, &jobs, tqe) { - if (!strcmp(j->label, launch_data_get_string(data))) { - job_start(j); - resp = launch_data_new_errno(0); - } - } - if (NULL == resp) - resp = launch_data_new_errno(ESRCH); - } else if (!strcmp(cmd, LAUNCH_KEY_STOPJOB)) { - TAILQ_FOREACH(j, &jobs, tqe) { - if (!strcmp(j->label, launch_data_get_string(data))) { - job_stop(j); - resp = launch_data_new_errno(0); - } - } - if (NULL == resp) - resp = launch_data_new_errno(ESRCH); - } else if (!strcmp(cmd, LAUNCH_KEY_REMOVEJOB)) { - TAILQ_FOREACH(j, &jobs, tqe) { - if (!strcmp(j->label, launch_data_get_string(data))) { - job_remove(j); - resp = launch_data_new_errno(0); - } - } - if (NULL == resp) - resp = launch_data_new_errno(ESRCH); - } else if (!strcmp(cmd, LAUNCH_KEY_SUBMITJOB)) { - if (launch_data_get_type(data) == LAUNCH_DATA_ARRAY) { - launch_data_t tmp; - size_t i; - - resp = launch_data_alloc(LAUNCH_DATA_ARRAY); - for (i = 0; i < launch_data_array_get_count(data); i++) { - tmp = load_job(launch_data_array_get_index(data, i)); - launch_data_array_set_index(resp, tmp, i); - } - } else { - resp = load_job(data); - } - } else if (!strcmp(cmd, LAUNCH_KEY_WORKAROUNDBONJOUR)) { - launch_data_dict_iterate(data, attach_bonjourfds_to_job, NULL); - resp = launch_data_new_errno(0); - } else if (!strcmp(cmd, LAUNCH_KEY_UNSETUSERENVIRONMENT)) { - unsetenv(launch_data_get_string(data)); - resp = launch_data_new_errno(0); - } else if (!strcmp(cmd, LAUNCH_KEY_GETUSERENVIRONMENT)) { - char **tmpenviron = environ; - resp = launch_data_alloc(LAUNCH_DATA_DICTIONARY); - for (; *tmpenviron; tmpenviron++) { - char envkey[1024]; - launch_data_t s = launch_data_alloc(LAUNCH_DATA_STRING); - launch_data_set_string(s, strchr(*tmpenviron, '=') + 1); - strncpy(envkey, *tmpenviron, sizeof(envkey)); - *(strchr(envkey, '=')) = '\0'; - launch_data_dict_insert(resp, s, envkey); - } - } else if (!strcmp(cmd, LAUNCH_KEY_SETUSERENVIRONMENT)) { - launch_data_dict_iterate(data, set_user_env, NULL); - resp = launch_data_new_errno(0); - } else if (!strcmp(cmd, LAUNCH_KEY_CHECKIN)) { - if (rmc->c->j) { - if (assumes((resp = launch_data_copy(rmc->c->j->ldj)) != NULL)) { - if (NULL == launch_data_dict_lookup(resp, LAUNCH_JOBKEY_TIMEOUT)) { - launch_data_t to = launch_data_new_integer(LAUNCHD_MIN_JOB_RUN_TIME); - launch_data_dict_insert(resp, to, LAUNCH_JOBKEY_TIMEOUT); - } - } - rmc->c->j->checkedin = true; - } else { - resp = launch_data_new_errno(EACCES); - } - } else if (!strcmp(cmd, LAUNCH_KEY_RELOADTTYS)) { - update_ttys(); - resp = launch_data_new_errno(0); - } else if (!strcmp(cmd, LAUNCH_KEY_SHUTDOWN)) { - do_shutdown(); - resp = launch_data_new_errno(0); - } else if (!strcmp(cmd, LAUNCH_KEY_GETJOBS)) { - resp = get_jobs(NULL); - launch_data_revoke_fds(resp); - } else if (!strcmp(cmd, LAUNCH_KEY_GETRESOURCELIMITS)) { - resp = adjust_rlimits(NULL); - } else if (!strcmp(cmd, LAUNCH_KEY_SETRESOURCELIMITS)) { - resp = adjust_rlimits(data); - } else if (!strcmp(cmd, LAUNCH_KEY_GETJOB)) { - resp = get_jobs(launch_data_get_string(data)); - launch_data_revoke_fds(resp); - } else if (!strcmp(cmd, LAUNCH_KEY_GETJOBWITHHANDLES)) { - resp = get_jobs(launch_data_get_string(data)); - } else if (!strcmp(cmd, LAUNCH_KEY_SETLOGMASK)) { - resp = launch_data_new_integer(setlogmask(launch_data_get_integer(data))); - } else if (!strcmp(cmd, LAUNCH_KEY_GETLOGMASK)) { - int oldmask = setlogmask(LOG_UPTO(LOG_DEBUG)); - resp = launch_data_new_integer(oldmask); - setlogmask(oldmask); - } else if (!strcmp(cmd, LAUNCH_KEY_SETUMASK)) { - resp = launch_data_new_integer(umask(launch_data_get_integer(data))); - } else if (!strcmp(cmd, LAUNCH_KEY_GETUMASK)) { - mode_t oldmask = umask(0); - resp = launch_data_new_integer(oldmask); - umask(oldmask); - } else if (!strcmp(cmd, LAUNCH_KEY_GETRUSAGESELF)) { - struct rusage rusage; - assumes(getrusage(RUSAGE_SELF, &rusage) != -1); - resp = launch_data_new_opaque(&rusage, sizeof(rusage)); - } else if (!strcmp(cmd, LAUNCH_KEY_GETRUSAGECHILDREN)) { - struct rusage rusage; - assumes(getrusage(RUSAGE_CHILDREN, &rusage) != -1); - resp = launch_data_new_opaque(&rusage, sizeof(rusage)); - } else if (!strcmp(cmd, LAUNCH_KEY_SETSTDOUT)) { - resp = setstdio(STDOUT_FILENO, data); - } else if (!strcmp(cmd, LAUNCH_KEY_SETSTDERR)) { - resp = setstdio(STDERR_FILENO, data); - } else if (!strcmp(cmd, LAUNCH_KEY_BATCHCONTROL)) { - batch_job_enable(launch_data_get_bool(data), rmc->c); - resp = launch_data_new_errno(0); - } else if (!strcmp(cmd, LAUNCH_KEY_BATCHQUERY)) { - resp = launch_data_alloc(LAUNCH_DATA_BOOL); - launch_data_set_bool(resp, batch_disabler_count == 0); - } - - rmc->resp = resp; -} - -static launch_data_t setstdio(int d, launch_data_t o) -{ - launch_data_t resp = launch_data_new_errno(0); - - if (launch_data_get_type(o) == LAUNCH_DATA_STRING) { - char **where = &pending_stderr; - if (d == STDOUT_FILENO) - where = &pending_stdout; - if (*where) - free(*where); - *where = strdup(launch_data_get_string(o)); - } else if (launch_data_get_type(o) == LAUNCH_DATA_FD) { - assumes(dup2(launch_data_get_fd(o), d) != -1); - } else { - launch_data_set_errno(resp, EINVAL); - } - - return resp; -} - -static void batch_job_enable(bool e, struct conncb *c) -{ - if (e && c->disabled_batch) { - batch_disabler_count--; - c->disabled_batch = 0; - if (batch_disabler_count == 0) - assumes(kevent_mod(asynckq, EVFILT_READ, EV_ENABLE, 0, 0, &kqasync_callback) != -1); - } else if (!e && !c->disabled_batch) { - if (batch_disabler_count == 0) - assumes(kevent_mod(asynckq, EVFILT_READ, EV_DISABLE, 0, 0, &kqasync_callback) != -1); - batch_disabler_count++; - c->disabled_batch = 1; - } -} - -static launch_data_t load_job(launch_data_t pload) -{ - launch_data_t tmp, resp; - const char *label; - struct jobcb *j; - bool startnow, hasprog = false, hasprogargs = false; - - if ((label = job_get_string(pload, LAUNCH_JOBKEY_LABEL))) { - TAILQ_FOREACH(j, &jobs, tqe) { - if (!strcmp(j->label, label)) { - resp = launch_data_new_errno(EEXIST); - goto out; - } - } - } else { - resp = launch_data_new_errno(EINVAL); - goto out; - } - - if (launch_data_dict_lookup(pload, LAUNCH_JOBKEY_PROGRAM)) - hasprog = true; - if (launch_data_dict_lookup(pload, LAUNCH_JOBKEY_PROGRAMARGUMENTS)) - hasprogargs = true; - - if (!hasprog && !hasprogargs) { - resp = launch_data_new_errno(EINVAL); - goto out; - } - - j = calloc(1, sizeof(struct jobcb) + strlen(label) + 1); - strcpy(j->label, label); - j->ldj = launch_data_copy(pload); - launch_data_revoke_fds(pload); - j->kqjob_callback = job_callback; - - - if (launch_data_dict_lookup(j->ldj, LAUNCH_JOBKEY_ONDEMAND) == NULL) { - tmp = launch_data_alloc(LAUNCH_DATA_BOOL); - launch_data_set_bool(tmp, true); - launch_data_dict_insert(j->ldj, tmp, LAUNCH_JOBKEY_ONDEMAND); - } - - TAILQ_INSERT_TAIL(&jobs, j, tqe); - - j->debug = job_get_bool(j->ldj, LAUNCH_JOBKEY_DEBUG); - - startnow = !job_get_bool(j->ldj, LAUNCH_JOBKEY_ONDEMAND); - - if (job_get_bool(j->ldj, LAUNCH_JOBKEY_RUNATLOAD)) - startnow = true; - - if ((tmp = launch_data_dict_lookup(j->ldj, LAUNCH_JOBKEY_QUEUEDIRECTORIES))) { - size_t i; - - j->qdirs_cnt = launch_data_array_get_count(tmp); - j->qdirs = malloc(sizeof(int) * j->qdirs_cnt); - - for (i = 0; i < j->qdirs_cnt; i++) { - const char *thepath = launch_data_get_string(launch_data_array_get_index(tmp, i)); - - if (-1 == (j->qdirs[i] = _fd(open(thepath, O_EVTONLY)))) - job_log_error(j, LOG_ERR, "open(\"%s\", O_EVTONLY)", thepath); - } - - } - - if ((tmp = launch_data_dict_lookup(j->ldj, LAUNCH_JOBKEY_STARTINTERVAL))) { - j->start_interval = launch_data_get_integer(tmp); - - if (j->start_interval == 0) { - job_log(j, LOG_WARNING, "StartInterval is zero, ignoring"); - } else { - assumes(kevent_mod((uintptr_t)&j->start_interval, EVFILT_TIMER, EV_ADD, NOTE_SECONDS, j->start_interval, j) != -1); - } - } - - if ((tmp = launch_data_dict_lookup(j->ldj, LAUNCH_JOBKEY_STARTCALENDARINTERVAL))) { - launch_data_t tmp_k; - - j->start_cal_interval = calloc(1, sizeof(struct tm)); - j->start_cal_interval->tm_min = -1; - j->start_cal_interval->tm_hour = -1; - j->start_cal_interval->tm_mday = -1; - j->start_cal_interval->tm_wday = -1; - j->start_cal_interval->tm_mon = -1; - - if (LAUNCH_DATA_DICTIONARY == launch_data_get_type(tmp)) { - if ((tmp_k = launch_data_dict_lookup(tmp, LAUNCH_JOBKEY_CAL_MINUTE))) - j->start_cal_interval->tm_min = launch_data_get_integer(tmp_k); - if ((tmp_k = launch_data_dict_lookup(tmp, LAUNCH_JOBKEY_CAL_HOUR))) - j->start_cal_interval->tm_hour = launch_data_get_integer(tmp_k); - if ((tmp_k = launch_data_dict_lookup(tmp, LAUNCH_JOBKEY_CAL_DAY))) - j->start_cal_interval->tm_mday = launch_data_get_integer(tmp_k); - if ((tmp_k = launch_data_dict_lookup(tmp, LAUNCH_JOBKEY_CAL_WEEKDAY))) - j->start_cal_interval->tm_wday = launch_data_get_integer(tmp_k); - if ((tmp_k = launch_data_dict_lookup(tmp, LAUNCH_JOBKEY_CAL_MONTH))) - j->start_cal_interval->tm_mon = launch_data_get_integer(tmp_k); - } - - job_set_alarm(j); - } - - if ((tmp = launch_data_dict_lookup(j->ldj, LAUNCH_JOBKEY_WATCHPATHS))) { - size_t i; - - j->vnodes_cnt = launch_data_array_get_count(tmp); - j->vnodes = malloc(sizeof(int) * j->vnodes_cnt); - - for (i = 0; i < j->vnodes_cnt; i++) { - const char *thepath = launch_data_get_string(launch_data_array_get_index(tmp, i)); - - assumes((j->vnodes[i] = _fd(open(thepath, O_EVTONLY))) != -1); - } - - } - - if ((tmp = launch_data_dict_lookup(j->ldj, LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES))) - launch_data_dict_iterate(tmp, setup_job_env, NULL); - - if (job_get_bool(j->ldj, LAUNCH_JOBKEY_ONDEMAND)) - job_watch(j); - - if (startnow) - job_start(j); - - resp = launch_data_new_errno(0); -out: - return resp; -} - -static launch_data_t get_jobs(const char *which) -{ - struct jobcb *j; - launch_data_t tmp, resp = NULL; - - if (which) { - TAILQ_FOREACH(j, &jobs, tqe) { - if (!strcmp(which, j->label)) - resp = launch_data_copy(j->ldj); - } - if (resp == NULL) - resp = launch_data_new_errno(ESRCH); - } else { - resp = launch_data_alloc(LAUNCH_DATA_DICTIONARY); - - TAILQ_FOREACH(j, &jobs, tqe) { - tmp = launch_data_copy(j->ldj); - launch_data_dict_insert(resp, tmp, j->label); - } - } - - return resp; -} - -static void usage(FILE *where) -{ - fprintf(where, "%s: [-d] [-- command [args ...]]\n", getprogname()); - fprintf(where, "\t-d\tdaemonize\n"); - fprintf(where, "\t-h\tthis usage statement\n"); - - if (where == stdout) - exit(EXIT_SUCCESS); -} - -int kevent_mod(uintptr_t ident, short filter, u_short flags, u_int fflags, intptr_t data, void *udata) -{ - struct kevent kev; - int q = mainkq; - - if (EVFILT_TIMER == filter || EVFILT_VNODE == filter) - q = asynckq; - - if (flags & EV_ADD && !assumes(udata != NULL)) { - errno = EINVAL; - return -1; - } - -#ifdef PID1_REAP_ADOPTED_CHILDREN - if (filter == EVFILT_PROC && getpid() == 1) - return 0; -#endif - EV_SET(&kev, ident, filter, flags, fflags, data, udata); - return kevent(q, &kev, 1, NULL, 0, NULL); -} - -static int _fd(int fd) -{ - if (fd >= 0) - assumes(fcntl(fd, F_SETFD, 1) != -1); - return fd; -} - -static void ipc_close(struct conncb *c) -{ - batch_job_enable(true, c); - - TAILQ_REMOVE(&connections, c, tqe); - launchd_close(c->conn); - free(c); -} - -static void setup_job_env(launch_data_t obj, const char *key, void *context __attribute__((unused))) -{ - if (LAUNCH_DATA_STRING == launch_data_get_type(obj)) - setenv(key, launch_data_get_string(obj), 1); -} - -static void unsetup_job_env(launch_data_t obj, const char *key, void *context __attribute__((unused))) -{ - if (LAUNCH_DATA_STRING == launch_data_get_type(obj)) - unsetenv(key); -} - -static void job_reap(struct jobcb *j) -{ - bool od = job_get_bool(j->ldj, LAUNCH_JOBKEY_ONDEMAND); - time_t td = time(NULL) - j->start_time; - bool bad_exit = false; - int status; - - job_log(j, LOG_DEBUG, "Reaping"); - - if (j->execfd) { - assumes(close(j->execfd) != -1); - j->execfd = 0; - } - -#ifdef PID1_REAP_ADOPTED_CHILDREN - if (getpid() == 1) - status = pid1_child_exit_status; - else -#endif - if (!assumes(waitpid(j->p, &status, 0) != -1)) { - return; - } - - if (WIFEXITED(status) && WEXITSTATUS(status) != 0) { - job_log(j, LOG_WARNING, "exited with exit code: %d", WEXITSTATUS(status)); - bad_exit = true; - } - - if (WIFSIGNALED(status)) { - int s = WTERMSIG(status); - if (SIGKILL == s || SIGTERM == s) { - job_log(j, LOG_NOTICE, "exited: %s", strsignal(s)); - } else { - job_log(j, LOG_WARNING, "exited abnormally: %s", strsignal(s)); - bad_exit = true; - } - } - - if (!od) { - if (td < LAUNCHD_MIN_JOB_RUN_TIME) { - job_log(j, LOG_WARNING, "respawning too quickly! throttling"); - bad_exit = true; - j->throttle = true; - } else if (td >= LAUNCHD_REWARD_JOB_RUN_TIME) { - job_log(j, LOG_INFO, "lived long enough, forgiving past exit failures"); - j->failed_exits = 0; - } - } - - if (bad_exit) - j->failed_exits++; - - if (j->failed_exits > 0) { - int failures_left = LAUNCHD_FAILED_EXITS_THRESHOLD - j->failed_exits; - if (failures_left) - job_log(j, LOG_WARNING, "%d more failure%s without living at least %d seconds will cause job removal", - failures_left, failures_left > 1 ? "s" : "", LAUNCHD_REWARD_JOB_RUN_TIME); - } - - total_children--; - j->p = 0; -} - -static bool job_restart_fitness_test(struct jobcb *j) -{ - bool od = job_get_bool(j->ldj, LAUNCH_JOBKEY_ONDEMAND); - - if (j->firstborn) { - job_log(j, LOG_DEBUG, "first born died, begin shutdown"); - do_shutdown(); - return false; - } else if (job_get_bool(j->ldj, LAUNCH_JOBKEY_SERVICEIPC) && !j->checkedin) { - job_log(j, LOG_WARNING, "failed to checkin"); - job_remove(j); - return false; - } else if (j->failed_exits >= LAUNCHD_FAILED_EXITS_THRESHOLD) { - job_log(j, LOG_WARNING, "too many failures in succession"); - job_remove(j); - return false; - } else if (od || shutdown_in_progress) { - if (!od && shutdown_in_progress) - job_log(j, LOG_NOTICE, "exited while shutdown is in progress, will not restart unless demand requires it"); - job_watch(j); - return false; - } - - return true; -} - -static void job_callback(void *obj, struct kevent *kev) -{ - struct jobcb *j = obj; - bool d = j->debug; - bool startnow = true; - int oldmask = 0; - - if (d) { - oldmask = setlogmask(LOG_UPTO(LOG_DEBUG)); - job_log(j, LOG_DEBUG, "log level debug temporarily enabled while processing job"); - } - - if (kev->filter == EVFILT_PROC) { - job_reap(j); - - startnow = job_restart_fitness_test(j); - - if (startnow && j->throttle) { - j->throttle = false; - job_log(j, LOG_WARNING, "will restart in %d seconds", LAUNCHD_MIN_JOB_RUN_TIME); - if (assumes(kevent_mod((uintptr_t)j, EVFILT_TIMER, EV_ADD|EV_ONESHOT, - NOTE_SECONDS, LAUNCHD_MIN_JOB_RUN_TIME, &j->kqjob_callback) != -1)) { - startnow = false; - } - } - } else if (kev->filter == EVFILT_TIMER && (void *)kev->ident == j->start_cal_interval) { - job_set_alarm(j); - } else if (kev->filter == EVFILT_VNODE) { - size_t i; - const char *thepath = NULL; - - for (i = 0; i < j->vnodes_cnt; i++) { - if (j->vnodes[i] == (int)kev->ident) { - launch_data_t ld_vnodes = launch_data_dict_lookup(j->ldj, LAUNCH_JOBKEY_WATCHPATHS); - - thepath = launch_data_get_string(launch_data_array_get_index(ld_vnodes, i)); - - job_log(j, LOG_DEBUG, "watch path modified: %s", thepath); - - if ((NOTE_DELETE|NOTE_RENAME|NOTE_REVOKE) & kev->fflags) { - job_log(j, LOG_DEBUG, "watch path invalidated: %s", thepath); - assumes(close(j->vnodes[i]) != -1); - j->vnodes[i] = -1; /* this will get fixed in job_watch() */ - } - } - } - - for (i = 0; i < j->qdirs_cnt; i++) { - if (j->qdirs[i] == (int)kev->ident) { - launch_data_t ld_qdirs = launch_data_dict_lookup(j->ldj, LAUNCH_JOBKEY_QUEUEDIRECTORIES); - int dcc_r; - - thepath = launch_data_get_string(launch_data_array_get_index(ld_qdirs, i)); - - job_log(j, LOG_DEBUG, "queue directory modified: %s", thepath); - - if (-1 == (dcc_r = dir_has_files(thepath))) { - job_log_error(j, LOG_ERR, "dir_has_files(\"%s\", ...)", thepath); - } else if (0 == dcc_r) { - job_log(j, LOG_DEBUG, "spurious wake up, directory empty: %s", thepath); - startnow = false; - } - } - } - /* if we get here, then the vnodes either wasn't a qdir, or if it was, it has entries in it */ - } else if (kev->filter == EVFILT_READ && (int)kev->ident == j->execfd) { - if (kev->data > 0) { - int e; - - assumes(read(j->execfd, &e, sizeof(e)) != -1); - errno = e; - job_log_error(j, LOG_ERR, "execve()"); - job_remove(j); - j = NULL; - startnow = false; - } else { - assumes(close(j->execfd) != -1); - j->execfd = 0; - } - startnow = false; - } - - if (startnow) - job_start(j); - - if (d) { - /* the job might have been removed, must not call job_log() */ - syslog(LOG_DEBUG, "restoring original log mask"); - setlogmask(oldmask); - } -} - -static void job_start(struct jobcb *j) -{ - int spair[2]; - int execspair[2]; - bool sipc; - char nbuf[64]; - pid_t c; - - job_log(j, LOG_DEBUG, "Starting"); - - if (j->p) { - job_log(j, LOG_DEBUG, "already running"); - return; - } - - j->checkedin = false; - - sipc = job_get_bool(j->ldj, LAUNCH_JOBKEY_SERVICEIPC); - - if (launch_data_dict_lookup(j->ldj, LAUNCH_JOBKEY_INETDCOMPATIBILITY)) - sipc = true; - - if (sipc) - assumes(socketpair(AF_UNIX, SOCK_STREAM, 0, spair) != -1); - - assumes(socketpair(AF_UNIX, SOCK_STREAM, 0, execspair) != -1); - - time(&j->start_time); - - switch (c = fork_with_bootstrap_port(launchd_bootstrap_port)) { - case -1: - job_log_error(j, LOG_ERR, "fork() failed, will try again in one second"); - assumes(close(execspair[0]) != -1); - assumes(close(execspair[1]) != -1);; - if (sipc) { - assumes(close(spair[0]) != -1); - assumes(close(spair[1]) != -1); - } - if (job_get_bool(j->ldj, LAUNCH_JOBKEY_ONDEMAND)) - job_ignore(j); - break; - case 0: - assumes(close(execspair[0]) != -1); - /* wait for our parent to say they've attached a kevent to us */ - assumes(read(_fd(execspair[1]), &c, sizeof(c)) != -1); - if (j->firstborn) { - setpgid(getpid(), getpid()); - if (isatty(STDIN_FILENO)) { - if (tcsetpgrp(STDIN_FILENO, getpid()) == -1) - job_log_error(j, LOG_WARNING, "tcsetpgrp()"); - } - } - - if (sipc) { - assumes(close(spair[0]) != -1); - sprintf(nbuf, "%d", spair[1]); - setenv(LAUNCHD_TRUSTED_FD_ENV, nbuf, 1); - } - job_start_child(j, execspair[1]); - break; - default: - assumes(close(execspair[1]) != -1); - j->execfd = _fd(execspair[0]); - if (sipc) { - assumes(close(spair[1]) != -1); - ipc_open(_fd(spair[0]), j); - } - assumes(kevent_mod(j->execfd, EVFILT_READ, EV_ADD, 0, 0, j) != -1); - - if (assumes(kevent_mod(c, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, j) != -1)) { - j->p = c; - total_children++; - if (job_get_bool(j->ldj, LAUNCH_JOBKEY_ONDEMAND)) - job_ignore(j); - } else { - job_reap(j); - } - /* this unblocks the child and avoids a race - * between the above fork() and the kevent_mod() */ - assumes(write(j->execfd, &c, sizeof(c)) != -1); - break; - } -} - -static void job_start_child(struct jobcb *j, int execfd) -{ - launch_data_t ldpa = launch_data_dict_lookup(j->ldj, LAUNCH_JOBKEY_PROGRAMARGUMENTS); - bool inetcompat = launch_data_dict_lookup(j->ldj, LAUNCH_JOBKEY_INETDCOMPATIBILITY) ? true : false; - size_t i, argv_cnt; - const char **argv, *file2exec = "/usr/libexec/launchproxy"; - int r; - bool hasprog = false; - - job_setup_attributes(j); - - if (ldpa) { - argv_cnt = launch_data_array_get_count(ldpa); - argv = alloca((argv_cnt + 2) * sizeof(char *)); - for (i = 0; i < argv_cnt; i++) - argv[i + 1] = launch_data_get_string(launch_data_array_get_index(ldpa, i)); - argv[argv_cnt + 1] = NULL; - } else { - argv = alloca(3 * sizeof(char *)); - argv[1] = job_get_string(j->ldj, LAUNCH_JOBKEY_PROGRAM); - argv[2] = NULL; - } - - if (job_get_string(j->ldj, LAUNCH_JOBKEY_PROGRAM)) - hasprog = true; - - if (inetcompat) { - argv[0] = file2exec; - } else { - argv++; - file2exec = job_get_file2exec(j->ldj); - } - - if (hasprog) { - r = execv(file2exec, (char *const*)argv); - } else { - r = execvp(file2exec, (char *const*)argv); - } - - if (-1 == r) { - assumes(write(execfd, &errno, sizeof(errno)) != -1); - job_log_error(j, LOG_ERR, "execv%s(\"%s\", ...)", hasprog ? "" : "p", file2exec); - } - _exit(EXIT_FAILURE); -} - -static void job_setup_attributes(struct jobcb *j) -{ - launch_data_t srl = launch_data_dict_lookup(j->ldj, LAUNCH_JOBKEY_SOFTRESOURCELIMITS); - launch_data_t hrl = launch_data_dict_lookup(j->ldj, LAUNCH_JOBKEY_HARDRESOURCELIMITS); - bool inetcompat = launch_data_dict_lookup(j->ldj, LAUNCH_JOBKEY_INETDCOMPATIBILITY) ? true : false; - launch_data_t tmp; - size_t i; - const char *tmpstr; - struct group *gre = NULL; - gid_t gre_g = 0; - static const struct { - const char *key; - int val; - } limits[] = { - { LAUNCH_JOBKEY_RESOURCELIMIT_CORE, RLIMIT_CORE }, - { LAUNCH_JOBKEY_RESOURCELIMIT_CPU, RLIMIT_CPU }, - { LAUNCH_JOBKEY_RESOURCELIMIT_DATA, RLIMIT_DATA }, - { LAUNCH_JOBKEY_RESOURCELIMIT_FSIZE, RLIMIT_FSIZE }, - { LAUNCH_JOBKEY_RESOURCELIMIT_MEMLOCK, RLIMIT_MEMLOCK }, - { LAUNCH_JOBKEY_RESOURCELIMIT_NOFILE, RLIMIT_NOFILE }, - { LAUNCH_JOBKEY_RESOURCELIMIT_NPROC, RLIMIT_NPROC }, - { LAUNCH_JOBKEY_RESOURCELIMIT_RSS, RLIMIT_RSS }, - { LAUNCH_JOBKEY_RESOURCELIMIT_STACK, RLIMIT_STACK }, - }; - - setpriority(PRIO_PROCESS, 0, job_get_integer(j->ldj, LAUNCH_JOBKEY_NICE)); - - if (srl || hrl) { - for (i = 0; i < (sizeof(limits) / sizeof(limits[0])); i++) { - struct rlimit rl; - - if (!assumes(getrlimit(limits[i].val, &rl) != -1)) { - continue; - } - - if (hrl) - rl.rlim_max = job_get_integer(hrl, limits[i].key); - if (srl) - rl.rlim_cur = job_get_integer(srl, limits[i].key); - - assumes(setrlimit(limits[i].val, &rl) != -1); - } - } - - if (!inetcompat && job_get_bool(j->ldj, LAUNCH_JOBKEY_SESSIONCREATE)) - launchd_SessionCreate(job_get_file2exec(j->ldj)); - - if (job_get_bool(j->ldj, LAUNCH_JOBKEY_LOWPRIORITYIO)) { - int lowprimib[] = { CTL_KERN, KERN_PROC_LOW_PRI_IO }; - int val = 1; - - assumes(sysctl(lowprimib, sizeof(lowprimib) / sizeof(lowprimib[0]), NULL, NULL, &val, sizeof(val)) != -1); - } - if ((tmpstr = job_get_string(j->ldj, LAUNCH_JOBKEY_ROOTDIRECTORY))) { - assumes(chroot(tmpstr) != -1); - assumes(chdir(".") != -1); - } - if ((tmpstr = job_get_string(j->ldj, LAUNCH_JOBKEY_GROUPNAME))) { - gre = getgrnam(tmpstr); - if (gre) { - gre_g = gre->gr_gid; - if (-1 == setgid(gre_g)) { - job_log_error(j, LOG_ERR, "setgid(%d)", gre_g); - exit(EXIT_FAILURE); - } - } else { - job_log(j, LOG_ERR, "getgrnam(\"%s\") failed", tmpstr); - exit(EXIT_FAILURE); - } - } - if ((tmpstr = job_get_string(j->ldj, LAUNCH_JOBKEY_USERNAME))) { - struct passwd *pwe = getpwnam(tmpstr); - if (pwe) { - uid_t pwe_u = pwe->pw_uid; - uid_t pwe_g = pwe->pw_gid; - - if (pwe->pw_expire && time(NULL) >= pwe->pw_expire) { - job_log(j, LOG_ERR, "expired account: %s", tmpstr); - exit(EXIT_FAILURE); - } - if (job_get_bool(j->ldj, LAUNCH_JOBKEY_INITGROUPS)) { - if (-1 == initgroups(tmpstr, gre ? gre_g : pwe_g)) { - job_log_error(j, LOG_ERR, "initgroups()"); - exit(EXIT_FAILURE); - } - } - if (!gre) { - if (-1 == setgid(pwe_g)) { - job_log_error(j, LOG_ERR, "setgid(%d)", pwe_g); - exit(EXIT_FAILURE); - } - } - if (-1 == setuid(pwe_u)) { - job_log_error(j, LOG_ERR, "setuid(%d)", pwe_u); - exit(EXIT_FAILURE); - } - } else { - job_log(j, LOG_WARNING, "getpwnam(\"%s\") failed", tmpstr); - exit(EXIT_FAILURE); - } - } - if ((tmpstr = job_get_string(j->ldj, LAUNCH_JOBKEY_WORKINGDIRECTORY))) - assumes(chdir(tmpstr) != -1); - if (launch_data_dict_lookup(j->ldj, LAUNCH_JOBKEY_UMASK)) - umask(job_get_integer(j->ldj, LAUNCH_JOBKEY_UMASK)); - if ((tmpstr = job_get_string(j->ldj, LAUNCH_JOBKEY_STANDARDOUTPATH))) { - int sofd = open(tmpstr, O_WRONLY|O_APPEND|O_CREAT, DEFFILEMODE); - if (assumes(sofd != -1)) { - assumes(dup2(sofd, STDOUT_FILENO) != -1); - assumes(close(sofd) != -1); - } - } - if ((tmpstr = job_get_string(j->ldj, LAUNCH_JOBKEY_STANDARDERRORPATH))) { - int sefd = open(tmpstr, O_WRONLY|O_APPEND|O_CREAT, DEFFILEMODE); - if (assumes(sefd != -1)) { - assumes(dup2(sefd, STDERR_FILENO) != -1); - assumes(close(sefd) != -1); - } - } - if ((tmp = launch_data_dict_lookup(j->ldj, LAUNCH_JOBKEY_ENVIRONMENTVARIABLES))) - launch_data_dict_iterate(tmp, setup_job_env, NULL); - - assumes(setsid() != -1); -} - -#ifdef PID1_REAP_ADOPTED_CHILDREN -__private_extern__ int pid1_child_exit_status = 0; -static void pid1waitpid(void) -{ - pid_t p; - - while ((p = waitpid(-1, &pid1_child_exit_status, WNOHANG)) > 0) { - if (!launchd_check_pid(p)) - init_check_pid(p); - } -} -#endif - -static void do_shutdown(void) -{ - struct jobcb *j; - - shutdown_in_progress = true; - - assumes(kevent_mod(asynckq, EVFILT_READ, EV_DISABLE, 0, 0, &kqasync_callback) != -1); - - TAILQ_FOREACH(j, &jobs, tqe) - job_stop(j); - - if (getpid() == 1) { - catatonia(); - mach_start_shutdown(SIGTERM); - } -} - -static void signal_callback(void *obj __attribute__((unused)), struct kevent *kev) -{ - switch (kev->ident) { - case SIGHUP: - update_ttys(); - reload_launchd_config(); - break; - case SIGTERM: - do_shutdown(); - break; -#ifdef PID1_REAP_ADOPTED_CHILDREN - case SIGCHLD: - /* Please automatically reap processes reparented to PID 1 */ - if (getpid() == 1) - pid1waitpid(); - break; -#endif - default: - break; - } -} - -static void fs_callback(void) -{ - static bool mounted_volfs = false; - - if (1 != getpid()) - mounted_volfs = true; - - if (pending_stdout) { - int fd = open(pending_stdout, O_CREAT|O_APPEND|O_WRONLY, DEFFILEMODE); - if (fd != -1) { - assumes(dup2(fd, STDOUT_FILENO) != -1); - assumes(close(fd) != -1); - free(pending_stdout); - pending_stdout = NULL; - } - } - if (pending_stderr) { - int fd = open(pending_stderr, O_CREAT|O_APPEND|O_WRONLY, DEFFILEMODE); - if (fd != -1) { - assumes(dup2(fd, STDERR_FILENO) != -1); - assumes(close(fd) != -1); - free(pending_stderr); - pending_stderr = NULL; - } - } - - if (!mounted_volfs) { - int r = mount("volfs", VOLFSDIR, MNT_RDONLY, NULL); - - if (-1 == r && errno == ENOENT) { - assumes(mkdir(VOLFSDIR, ACCESSPERMS & ~(S_IWUSR|S_IWGRP|S_IWOTH)) != -1); - r = mount("volfs", VOLFSDIR, MNT_RDONLY, NULL); - } - - if (-1 == r) { - syslog(LOG_WARNING, "mount(\"%s\", \"%s\", ...): %m", "volfs", VOLFSDIR); - } else { - mounted_volfs = true; - } - } - - if (!launchd_inited) - launchd_server_init(false); -} - -static void readcfg_callback(void *obj __attribute__((unused)), struct kevent *kev __attribute__((unused))) -{ - int status; - -#ifdef PID1_REAP_ADOPTED_CHILDREN - if (getpid() == 1) - status = pid1_child_exit_status; - else -#endif - if (-1 == waitpid(readcfg_pid, &status, 0)) { - syslog(LOG_WARNING, "waitpid(readcfg_pid, ...): %m"); - return; - } - - readcfg_pid = 0; - - if (WIFEXITED(status)) { - if (WEXITSTATUS(status)) - syslog(LOG_WARNING, "Unable to read launchd.conf: launchctl exited with status: %d", WEXITSTATUS(status)); - } else if (WIFSIGNALED(status)) { - syslog(LOG_WARNING, "Unable to read launchd.conf: launchctl exited abnormally: %s", strsignal(WTERMSIG(status))); - } else { - syslog(LOG_WARNING, "Unable to read launchd.conf: launchctl exited abnormally"); - } -} - -static void reload_launchd_config(void) -{ - struct stat sb; - static char *ldconf = PID1LAUNCHD_CONF; - const char *h = getenv("HOME"); - - if (h && ldconf == PID1LAUNCHD_CONF) - asprintf(&ldconf, "%s/%s", h, LAUNCHD_CONF); - - if (!ldconf) - return; - - if (lstat(ldconf, &sb) == 0) { - int spair[2]; - assumes(socketpair(AF_UNIX, SOCK_STREAM, 0, spair) != -1); - readcfg_pid = fork_with_bootstrap_port(launchd_bootstrap_port); - if (readcfg_pid == 0) { - char nbuf[100]; - assumes(close(spair[0]) != -1); - sprintf(nbuf, "%d", spair[1]); - setenv(LAUNCHD_TRUSTED_FD_ENV, nbuf, 1); - int fd = open(ldconf, O_RDONLY); - if (fd == -1) { - syslog(LOG_ERR, "open(\"%s\"): %m", ldconf); - exit(EXIT_FAILURE); - } - assumes(dup2(fd, STDIN_FILENO) != -1); - assumes(close(fd) != -1); - assumes(execl(LAUNCHCTL_PATH, LAUNCHCTL_PATH, NULL) != -1); - _exit(EXIT_FAILURE); - } else if (readcfg_pid == -1) { - assumes(close(spair[0]) != -1); - assumes(close(spair[1]) != -1); - syslog(LOG_ERR, "fork(): %m"); - readcfg_pid = 0; - } else { - assumes(close(spair[1]) != -1); - ipc_open(_fd(spair[0]), NULL); - assumes(kevent_mod(readcfg_pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, &kqreadcfg_callback) != -1); - } - } -} - -static void conceive_firstborn(char *argv[]) -{ - launch_data_t r, d = launch_data_alloc(LAUNCH_DATA_DICTIONARY); - launch_data_t args = launch_data_alloc(LAUNCH_DATA_ARRAY); - launch_data_t l = launch_data_new_string("com.apple.launchd.firstborn"); - size_t i; - - for (i = 0; *argv; argv++, i++) - launch_data_array_set_index(args, launch_data_new_string(*argv), i); - - launch_data_dict_insert(d, args, LAUNCH_JOBKEY_PROGRAMARGUMENTS); - launch_data_dict_insert(d, l, LAUNCH_JOBKEY_LABEL); - - r = load_job(d); - - launch_data_free(r); - launch_data_free(d); - - TAILQ_FIRST(&jobs)->firstborn = true; -} - -static void loopback_setup(void) -{ - struct ifaliasreq ifra; - struct in6_aliasreq ifra6; - struct ifreq ifr; - int s, s6; - - memset(&ifr, 0, sizeof(ifr)); - strcpy(ifr.ifr_name, "lo0"); - - assumes((s = socket(AF_INET, SOCK_DGRAM, 0)) != -1); - assumes((s6 = socket(AF_INET6, SOCK_DGRAM, 0)) != -1); - - if (ioctl(s, SIOCGIFFLAGS, &ifr) == -1) { - syslog(LOG_ERR, "ioctl(SIOCGIFFLAGS): %m"); - } else { - ifr.ifr_flags |= IFF_UP; - - assumes(ioctl(s, SIOCSIFFLAGS, &ifr) != -1); - } - - memset(&ifr, 0, sizeof(ifr)); - strcpy(ifr.ifr_name, "lo0"); - - if (ioctl(s6, SIOCGIFFLAGS, &ifr) == -1) { - syslog(LOG_ERR, "ioctl(SIOCGIFFLAGS): %m"); - } else { - ifr.ifr_flags |= IFF_UP; - - assumes(ioctl(s6, SIOCSIFFLAGS, &ifr) != -1); - } - - memset(&ifra, 0, sizeof(ifra)); - strcpy(ifra.ifra_name, "lo0"); - - ((struct sockaddr_in *)&ifra.ifra_addr)->sin_family = AF_INET; - ((struct sockaddr_in *)&ifra.ifra_addr)->sin_addr.s_addr = htonl(INADDR_LOOPBACK); - ((struct sockaddr_in *)&ifra.ifra_addr)->sin_len = sizeof(struct sockaddr_in); - ((struct sockaddr_in *)&ifra.ifra_mask)->sin_family = AF_INET; - ((struct sockaddr_in *)&ifra.ifra_mask)->sin_addr.s_addr = htonl(IN_CLASSA_NET); - ((struct sockaddr_in *)&ifra.ifra_mask)->sin_len = sizeof(struct sockaddr_in); - - assumes(ioctl(s, SIOCAIFADDR, &ifra) != -1); - - memset(&ifra6, 0, sizeof(ifra6)); - strcpy(ifra6.ifra_name, "lo0"); - - ifra6.ifra_addr.sin6_family = AF_INET6; - ifra6.ifra_addr.sin6_addr = in6addr_loopback; - ifra6.ifra_addr.sin6_len = sizeof(struct sockaddr_in6); - ifra6.ifra_prefixmask.sin6_family = AF_INET6; - memset(&ifra6.ifra_prefixmask.sin6_addr, 0xff, sizeof(struct in6_addr)); - ifra6.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6); - ifra6.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME; - ifra6.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME; - - assumes(ioctl(s6, SIOCAIFADDR_IN6, &ifra6) != -1); - - assumes(close(s) != -1); - assumes(close(s6) != -1); -} - -static void workaround3048875(int argc, char *argv[]) -{ - int i; - char **ap, *newargv[100], *p = argv[1]; - - if (argc == 1 || argc > 2) - return; - - newargv[0] = argv[0]; - for (ap = newargv + 1, i = 1; ap < &newargv[100]; ap++, i++) { - if ((*ap = strsep(&p, " \t")) == NULL) - break; - if (**ap == '\0') { - *ap = NULL; - break; - } - } - - if (argc == i) + if (!launchd_assumes(ioctl(pfs, SIOCSKEVFILT, &kev_req) != -1)) { + close(pfs); return; - - execv(newargv[0], newargv); -} - -static launch_data_t adjust_rlimits(launch_data_t in) -{ - static struct rlimit *l = NULL; - static size_t lsz = sizeof(struct rlimit) * RLIM_NLIMITS; - struct rlimit *ltmp; - size_t i,ltmpsz; - - if (l == NULL) { - l = malloc(lsz); - for (i = 0; i < RLIM_NLIMITS; i++) { - assumes(getrlimit(i, l + i) != -1); - } - } - - if (in) { - ltmp = launch_data_get_opaque(in); - ltmpsz = launch_data_get_opaque_size(in); - - if (ltmpsz > lsz) { - syslog(LOG_WARNING, "Too much rlimit data sent!"); - ltmpsz = lsz; - } - - for (i = 0; i < (ltmpsz / sizeof(struct rlimit)); i++) { - if (ltmp[i].rlim_cur == l[i].rlim_cur && ltmp[i].rlim_max == l[i].rlim_max) - continue; - - if (readcfg_pid && getpid() == 1) { - int gmib[] = { CTL_KERN, KERN_MAXPROC }; - int pmib[] = { CTL_KERN, KERN_MAXPROCPERUID }; - const char *gstr = "kern.maxproc"; - const char *pstr = "kern.maxprocperuid"; - int gval = ltmp[i].rlim_max; - int pval = ltmp[i].rlim_cur; - switch (i) { - case RLIMIT_NOFILE: - gmib[1] = KERN_MAXFILES; - pmib[1] = KERN_MAXFILESPERPROC; - gstr = "kern.maxfiles"; - pstr = "kern.maxfilesperproc"; - break; - case RLIMIT_NPROC: - /* kernel will not clamp to this value, we must */ - if (gval > (2048 + 20)) - gval = 2048 + 20; - break; - default: - break; - } - assumes(sysctl(gmib, 2, NULL, NULL, &gval, sizeof(gval)) != -1); - assumes(sysctl(pmib, 2, NULL, NULL, &pval, sizeof(pval)) != -1); - } - assumes(setrlimit(i, ltmp + i) != -1); - /* the kernel may have clamped the values we gave it */ - assumes(getrlimit(i, l + i) != -1); - } - } - - return launch_data_new_opaque(l, sizeof(struct rlimit) * RLIM_NLIMITS); -} - -__private_extern__ void launchd_SessionCreate(const char *who) -{ - void *seclib = dlopen(SECURITY_LIB, RTLD_LAZY); - OSStatus (*sescr)(SessionCreationFlags flags, SessionAttributeBits attributes); - - if (seclib) { - sescr = dlsym(seclib, "SessionCreate"); - - if (sescr) { - OSStatus scr = sescr(0, 0); - if (scr != noErr) - syslog(LOG_WARNING, "%s: SessionCreate() failed: %d", who, scr); - } else { - syslog(LOG_WARNING, "%s: couldn't find SessionCreate() in %s", who, SECURITY_LIB); - } - - dlclose(seclib); - } else { - syslog(LOG_WARNING, "%s: dlopen(\"%s\",...): %s", who, SECURITY_LIB, dlerror()); - } -} - -static int dir_has_files(const char *path) -{ - DIR *dd = opendir(path); - struct dirent *de; - bool r = 0; - - if (!dd) - return -1; - - while ((de = readdir(dd))) { - if (strcmp(de->d_name, ".") && strcmp(de->d_name, "..")) { - r = 1; - break; - } } - closedir(dd); - return r; + launchd_assumes(kevent_mod(pfs, EVFILT_READ, EV_ADD, 0, 0, &kqpfsystem_callback) != -1); } -static void job_set_alarm(struct jobcb *j) +void +pfsystem_callback(void *obj, struct kevent *kev) { - time_t later; - - later = cronemu(j->start_cal_interval->tm_mon, j->start_cal_interval->tm_mday, - j->start_cal_interval->tm_hour, j->start_cal_interval->tm_min); + bool new_networking_state; + char buf[1024]; - if (j->start_cal_interval->tm_wday != -1) { - time_t otherlater = cronemu_wday(j->start_cal_interval->tm_wday, - j->start_cal_interval->tm_hour, j->start_cal_interval->tm_min); + launchd_assumes(read(kev->ident, &buf, sizeof(buf)) != -1); - if (-1 != j->start_cal_interval->tm_mday) { - later = later < otherlater ? later : otherlater; - } else { - later = otherlater; - } - } + new_networking_state = get_network_state(); - if (-1 == kevent_mod((uintptr_t)j->start_cal_interval, EVFILT_TIMER, EV_ADD, NOTE_ABSOLUTE|NOTE_SECONDS, later, j)) { - job_log_error(j, LOG_ERR, "adding kevent alarm"); - } else { - job_log(j, LOG_INFO, "scheduled to run again at: %s", ctime(&later)); + if (new_networking_state != network_up) { + network_up = new_networking_state; + job_dispatch_all_other_semaphores(root_job, NULL); } } void -job_prep_log_msg(struct jobcb *j, char *buf, const char *msg, int err) +_log_launchd_bug(const char *rcs_rev, const char *path, unsigned int line, const char *test) { - size_t lsz = strlen(j->label); - size_t i, o; - - for (i = 0, o = 0; i < lsz; i++, o++) { - if (j->label[i] == '%') { - buf[o] = '%'; - o++; - buf[o] = '%'; - } else { - buf[o] = j->label[i]; - } - } - - buf[o++] = ':'; - buf[o++] = ' '; + int saved_errno = errno; + char buf[100]; + const char *file = strrchr(path, '/'); + char *rcs_rev_tmp = strchr(rcs_rev, ' '); - if (err) { - sprintf(buf + o, "%s: %s", msg, strerror(err)); + if (!file) { + file = path; } else { - strcpy(buf + o, msg); - } -} - -void -job_log_error(struct jobcb *j, int pri, const char *msg, ...) -{ - char newmsg[10000]; - va_list ap; - - job_prep_log_msg(j, newmsg, msg, errno); - - va_start(ap, msg); - - vsyslog(pri, newmsg, ap); - - va_end(ap); -} - -void -job_log(struct jobcb *j, int pri, const char *msg, ...) -{ - char newmsg[10000]; - va_list ap; - - job_prep_log_msg(j, newmsg, msg, 0); - - va_start(ap, msg); - - vsyslog(pri, newmsg, ap); - - va_end(ap); -} - -static void async_callback(void) -{ - struct timespec timeout = { 0, 0 }; - struct kevent kev; - - switch (kevent(asynckq, NULL, 0, &kev, 1, &timeout)) { - case -1: - syslog(LOG_DEBUG, "kevent(): %m"); - break; - case 1: - (*((kq_callback *)kev.udata))(kev.udata, &kev); - case 0: - break; - default: - syslog(LOG_DEBUG, "unexpected: kevent() returned something != 0, -1 or 1"); + file += 1; } -} - -static void testfd_or_openfd(int fd, const char *path, int flags) -{ - int tmpfd; - if (-1 != (tmpfd = dup(fd))) { - assumes(close(tmpfd) != -1); + if (!rcs_rev_tmp) { + strlcpy(buf, rcs_rev, sizeof(buf)); } else { - if (-1 == (tmpfd = open(path, flags))) { - syslog(LOG_ERR, "open(\"%s\", ...): %m", path); - } else if (tmpfd != fd) { - assumes(dup2(tmpfd, fd) != -1); - assumes(close(tmpfd) != -1); - } - } -} - -time_t -cronemu(int mon, int mday, int hour, int min) -{ - struct tm workingtm; - time_t now; - - now = time(NULL); - workingtm = *localtime(&now); - - workingtm.tm_isdst = -1; - workingtm.tm_sec = 0; - workingtm.tm_min++; - - while (!cronemu_mon(&workingtm, mon, mday, hour, min)) { - workingtm.tm_year++; - workingtm.tm_mon = 0; - workingtm.tm_mday = 1; - workingtm.tm_hour = 0; - workingtm.tm_min = 0; - mktime(&workingtm); - } - - return mktime(&workingtm); -} - -time_t -cronemu_wday(int wday, int hour, int min) -{ - struct tm workingtm; - time_t now; - - now = time(NULL); - workingtm = *localtime(&now); - - workingtm.tm_isdst = -1; - workingtm.tm_sec = 0; - workingtm.tm_min++; - - if (wday == 7) - wday = 0; - - while (workingtm.tm_wday != wday || !cronemu_hour(&workingtm, hour, min)) { - workingtm.tm_mday++; - workingtm.tm_hour = 0; - workingtm.tm_min = 0; - cronemu_hour(&workingtm, hour, min); - mktime(&workingtm); + strlcpy(buf, rcs_rev_tmp + 1, sizeof(buf)); + rcs_rev_tmp = strchr(buf, ' '); + if (rcs_rev_tmp) + *rcs_rev_tmp = '\0'; } - return mktime(&workingtm); + syslog(LOG_NOTICE, "Bug: %s:%u (%s):%u: %s", file, line, buf, saved_errno, test); } bool -cronemu_mon(struct tm *wtm, int mon, int mday, int hour, int min) +progeny_check(pid_t p) { - if (mon == -1) { - struct tm workingtm = *wtm; - int carrytest; - - while (!cronemu_mday(&workingtm, mday, hour, min)) { - workingtm.tm_mon++; - workingtm.tm_mday = 1; - workingtm.tm_hour = 0; - workingtm.tm_min = 0; - carrytest = workingtm.tm_mon; - mktime(&workingtm); - if (carrytest != workingtm.tm_mon) - return false; - } - *wtm = workingtm; - return true; - } - - if (mon < wtm->tm_mon) - return false; - - if (mon > wtm->tm_mon) { - wtm->tm_mon = mon; - wtm->tm_mday = 1; - wtm->tm_hour = 0; - wtm->tm_min = 0; - } + pid_t selfpid = getpid(); - return cronemu_mday(wtm, mday, hour, min); -} + while (p != selfpid && p != 1) { + int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, p }; + size_t miblen = sizeof(mib) / sizeof(mib[0]); + struct kinfo_proc kp; + size_t kplen = sizeof(kp); -bool -cronemu_mday(struct tm *wtm, int mday, int hour, int min) -{ - if (mday == -1) { - struct tm workingtm = *wtm; - int carrytest; - - while (!cronemu_hour(&workingtm, hour, min)) { - workingtm.tm_mday++; - workingtm.tm_hour = 0; - workingtm.tm_min = 0; - carrytest = workingtm.tm_mday; - mktime(&workingtm); - if (carrytest != workingtm.tm_mday) - return false; + if (launchd_assumes(sysctl(mib, miblen, &kp, &kplen, NULL, 0) != -1) + && launchd_assumes(kplen == sizeof(kp))) { + p = kp.kp_eproc.e_ppid; + } else { + return false; } - *wtm = workingtm; - return true; - } - - if (mday < wtm->tm_mday) - return false; - - if (mday > wtm->tm_mday) { - wtm->tm_mday = mday; - wtm->tm_hour = 0; - wtm->tm_min = 0; } - return cronemu_hour(wtm, hour, min); -} - -bool -cronemu_hour(struct tm *wtm, int hour, int min) -{ - if (hour == -1) { - struct tm workingtm = *wtm; - int carrytest; - - while (!cronemu_min(&workingtm, min)) { - workingtm.tm_hour++; - workingtm.tm_min = 0; - carrytest = workingtm.tm_hour; - mktime(&workingtm); - if (carrytest != workingtm.tm_hour) - return false; - } - *wtm = workingtm; + if (p == selfpid) return true; - } - - if (hour < wtm->tm_hour) - return false; - - if (hour > wtm->tm_hour) { - wtm->tm_hour = hour; - wtm->tm_min = 0; - } - return cronemu_min(wtm, min); + return false; } -bool -cronemu_min(struct tm *wtm, int min) +boolean_t +launchd_internal_demux(mach_msg_header_t *Request, mach_msg_header_t *Reply) { - if (min == -1) - return true; - - if (min < wtm->tm_min) - return false; - - if (min > wtm->tm_min) { - wtm->tm_min = min; + if (gc_this_job) { + job_remove(gc_this_job); + gc_this_job = NULL; } - return true; -} - -void -_log_launchd_bug(const char *path, unsigned int line, const char *test) -{ - int saved_errno = errno; - const char *file = strrchr(path, '/'); - - if (!file) { - file = path; + if (Request->msgh_local_port == launchd_internal_port) { + if (launchd_internal_server_routine(Request)) + return launchd_internal_server(Request, Reply); } else { - file += 1; + if (bootstrap_server_routine(Request)) + return bootstrap_server(Request, Reply); } - syslog(LOG_NOTICE, "Bug: %s:%u:%u: %s", file, line, saved_errno, test); + return notify_server(Request, Reply); } diff --git a/launchd/src/launchd.h b/launchd/src/launchd.h index 7292c88..cfc0c55 100644 --- a/launchd/src/launchd.h +++ b/launchd/src/launchd.h @@ -1,55 +1,95 @@ /* * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ + * @APPLE_APACHE_LICENSE_HEADER_START@ * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * The 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, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and * limitations under the License. * - * @APPLE_LICENSE_HEADER_END@ + * @APPLE_APACHE_LICENSE_HEADER_END@ */ #ifndef __LAUNCHD_H__ #define __LAUNCHD_H__ -#include +#include +#include +#include "launch.h" +#include "bootstrap_public.h" + +/* + * Use launchd_assumes() when we can recover, even if it means we leak or limp along. + * + * Use launchd_assert() for core initialization routines. + */ +#define launchd_assumes(e) \ + (__builtin_expect(!(e), 0) ? _log_launchd_bug(__rcs_file_version__, __FILE__, __LINE__, #e), false : true) + +#define launchd_blame(e, b) \ + (__builtin_expect(!(e), 0) ? syslog(LOG_DEBUG, "Encountered bug: %d", b), false : true) -#define assumes(e) \ - (__builtin_expect(!(e), 0) ? _log_launchd_bug(__FILE__, __LINE__, #e), false : true) +#define launchd_assert(e) launchd_assumes(e) ? true : abort(); -void _log_launchd_bug(const char *path, unsigned int line, const char *test); +#define FIRSTBORN_LABEL "com.apple.launchd.firstborn" +#define READCONF_LABEL "com.apple.launchd.readconfig" + +struct kevent; +struct conncb; typedef void (*kq_callback)(void *, struct kevent *); extern kq_callback kqsimple_zombie_reaper; -extern mach_port_t launchd_bootstrap_port; extern sigset_t blocked_signals; -extern pthread_mutex_t giant_lock; +extern bool shutdown_in_progress; +extern bool network_up; +extern int batch_disabler_count; +extern mach_port_t launchd_internal_port; +extern mach_port_t ipc_port_set; -#ifdef PID1_REAP_ADOPTED_CHILDREN -extern int pid1_child_exit_status; -#endif +bool init_check_pid(pid_t); int kevent_mod(uintptr_t ident, short filter, u_short flags, u_int fflags, intptr_t data, void *udata); -void launchd_SessionCreate(const char *who); +void batch_job_enable(bool e, struct conncb *c); + +launch_data_t launchd_setstdio(int d, launch_data_t o); +void launchd_SessionCreate(void); +void launchd_shutdown(void); +void launchd_single_user(void); +pid_t launchd_fork(void); +boolean_t launchd_mach_ipc_demux(mach_msg_header_t *Request, mach_msg_header_t *Reply); -void init_boot(bool sflag, bool vflag, bool xflag); +kern_return_t launchd_set_bport(mach_port_t name); +kern_return_t launchd_get_bport(mach_port_t *name); +kern_return_t launchd_mport_notify_req(mach_port_t name, mach_msg_id_t which); +kern_return_t launchd_mport_notify_cancel(mach_port_t name, mach_msg_id_t which); +kern_return_t launchd_mport_request_callback(mach_port_t name, void *obj, bool readmsg); +kern_return_t launchd_mport_create_recv(mach_port_t *name); +kern_return_t launchd_mport_deallocate(mach_port_t name); +kern_return_t launchd_mport_make_send(mach_port_t name); +kern_return_t launchd_mport_close_recv(mach_port_t name); + +void init_boot(bool sflag); void init_pre_kevent(void); -bool init_check_pid(pid_t); void update_ttys(void); void catatonia(void); -void death(void); + +void mach_start_shutdown(void); +void mach_init_init(mach_port_t, mach_port_t, name_array_t, mach_port_array_t, mach_msg_type_number_t); +void mach_init_reap(void); + +int _fd(int fd); + +void _log_launchd_bug(const char *rcs_rev, const char *path, unsigned int line, const char *test); + +bool progeny_check(pid_t p); #endif diff --git a/launchd/src/launchd.plist.5 b/launchd/src/launchd.plist.5 index f6c1f2d..7105f01 100644 --- a/launchd/src/launchd.plist.5 +++ b/launchd/src/launchd.plist.5 @@ -21,14 +21,17 @@ MUST NOT do the following in the process directly launched by .Pp .Bl -bullet -offset indent -compact .It +Call +.Xr daemon 3 . +.It +Do the moral equivalent of +.Xr daemon 3 +by calling .Xr fork 2 and have the parent process .Xr exit 3 or .Xr _exit 2 . -.It -Call -.Xr daemon 3 .El .Pp A daemon or agent launched by @@ -67,14 +70,15 @@ SHOULD: .Bl -bullet -offset indent -compact .It Launch on demand given criteria specified in the XML property list. -More information can be found in -.Xr launch 3 . +More information can be found later in this man page. .It Catch the SIGTERM signal. .El .Sh XML PROPERTY LIST KEYS The follow keys can be used to describe the configuration details of your daemon or agent. -Property lists files are expected to have their name end in ".plist" but that is not strictly required. +Property lists are Apple's standard configuration file format. Please see +.Xr plist 5 +for more information. Please note. Property list files are expected to have their name end in ".plist". .Pp .Bl -ohang .It Sy Label @@ -83,20 +87,25 @@ This required key uniquely identifies the job to .It Sy Disabled This optional key is used to disable your job. The default is false. .It Sy UserName -This optional key specifies the user to run the job as. The default is the user who submitted the job to -.Nm launchd . +This optional key specifies the user to run the job as. This key is only applicable when launchd is running as root. .It Sy GroupName -This optional key specifies the user to run the job as. The default is the group of the user who submitted the job to -.Nm launchd . +This optional key specifies the group to run the job as. This key is only applicable when launchd is running as root. If UserName is set and GroupName is not, the the group will be set to the default group of the user. .It Sy inetdCompatibility -The presence of this key specifies that the daemon expects to be run as if it were launched from -.Xr inetd 8 . -This flag is incompatible with the ServiceIPC key. +The presence of this key specifies that the daemon expects to be run as if it were launched from inetd. .Bl -ohang -offset indent .It Sy Wait -This flag corresponds to the "wait" or "nowait" option of -.Xr inetd 8 . +This flag corresponds to the "wait" or "nowait" option of inetd. If true, then the listening socket is passed via the standard in/out/error file descriptors. If false, then +.Xr accept 2 +is called on behalf of the job, and the result is passed via the standard in/out/error descriptors. .El +.It Sy LimitLoadToHosts +This configuration file only applies to the hosts listed with this key. +.It Sy LimitLoadFromHosts +This configuration file only applies to hosts NOT listed with this key. +.It Sy LimitLoadToSessionType +This configuration file only applies to sessions of the type specified. This key is used +in concert with the -S flag to +.Nm launchctl . .It Sy ProgramArguments This required key maps to the second argument of .Xr execvp 3 . @@ -104,32 +113,63 @@ This required key maps to the second argument of This optional key maps to the first argument of .Xr execvp 3 . If this key is missing, then the first element of the array of strings provided to the ProgramArguments will be used instead. +.It Sy EnableGlobbing +This flag causes +.Nm launchd +to use the +.Xr glob 3 +mechanism to update the program arguments before invocation. .It Sy OnDemand -This optional key is used to control whether your job is launched based on demand or to be kept continuously running. The default is true. +This key was used in Mac OS X 10.4 to control whether a job was kept alive or not. The default was true. +This key has been deprecated and replaced in Mac OS X 10.5 with the more powerful KeepAlive option. +.It Sy KeepAlive +This optional key is used to control whether your job is to be kept +continuously running or to let demand and conditions control the invocation. The +default is false and therefore only demand will start the job. The value may be +set to true to unconditionally keep the job alive. Alternatively, a dictionary +of conditions may be specified to selectively control whether +.Nm launchd +keeps a job alive or not. If multiple keys are provided, launchd ORs them, thus +providing maximum flexibility to the job to refine the logic and stall if necessary. If +.Nm launchd +finds no reason to restart the job, it falls back on demand based invocation. +Jobs that exit quickly and frequently when configured to be kept alive will be +throttled to converve system resources. +.Bl -ohang -offset indent +.It Sy SuccessfulExit +If true, the job will be restarted as long as the program exits and with an exit +status of zero. If false, the job will be restarted in the inverse condition. +This key implies that "RunAtLoad" is set to true, since the job needs to run at +least once before we can get an exit status. +.It Sy NetworkState +If true, the job will be kept alive as long as the network is up, where up is +defined as at least one non-loopback interface being up and having IPv4 or IPv6 +addresses assigned to them. +If false, the job will be kept alive in the inverse condition. +.It Sy PathState +Each key in this dictionary is a file-system path. If the value of the key is +true, then the job will be kept alive as long as the path exists. +If false, the job will be kept alive in the inverse condition. The intent of this +feature is that two or more jobs may create semaphores in the file-system namespace. +.El .It Sy RunAtLoad This optional key is used to control whether your job is launched once at the time the job is loaded. The default is false. .It Sy RootDirectory -This optional key is used to specific a directory to +This optional key is used to specify a directory to .Xr chroot 2 to before running the job. .It Sy WorkingDirectory -This optional key is used to specific a directory to +This optional key is used to specify a directory to .Xr chdir 2 to before running the job. -.It Sy ServiceDescription -This optional key is used to specify a human readable description of the purpose of the job. .It Sy EnvironmentVariables This optional key is used to specify additional environmental variables to be setup before running the job. .It Sy Umask This optional key specifies what value should be passed to .Xr umask 2 -before running the job. -.It Sy ServiceIPC -This optional key specifies whether the job participates in advanced communication with -.Nm launchd . -The default is false. This flag is incompatible with the inetdCompatibility key. +before running the job. Known bug: Property lists don't support octal, so please convert the value to decimal. .It Sy TimeOut -The recommended time out to pass to the job. If no value is specified, a default time out will be supplied by +The recommended time out (in seconds) to pass to the job. If no value is specified, a default time out will be supplied by .Nm launchd for use by the job at check in time. .It Sy InitGroups @@ -146,7 +186,7 @@ This optional key causes the job to be started every N seconds. If the system is asleep, the job will be started the next time the computer wakes up. If multiple intervals transpire before the computer is woken, those events will be coalesced into one event upon wake from sleep. -.It Sy StartCalendarInterval +.It Sy StartCalendarInterval This optional key causes the job to be started every calendar interval as specified. Missing arguments are considered to be wildcard. The semantics are much like .Xr crontab 5 . Unlike cron which skips job invocations when the computer is asleep, launchd @@ -175,6 +215,10 @@ This optional key specifies what file should be used for data being sent to stde This optional key specifies that .Nm launchd should adjust its log mask temporarily to LOG_DEBUG while dealing with this job. +.It Sy WaitForDebugger +This optional key specifies that +.Nm launchd +should instruct the kernel to have the job wait for a debugger to attach before any code in the job is executed. .It Sy SoftResourceLimits .It Sy HardResourceLimits Resource limits to be imposed on the job. These adjust variables set with @@ -215,10 +259,24 @@ This optional key specifies what value should be applied to the daemon. .It Sy LowPriorityIO This optional key specifies whether the kernel should consider this daemon to be low priority when doing file system I/O. +.It Sy MachServices +This optional key is used to specify Mach services to be registered with the +Mach bootstrap sub-system. Each key in this dictionary should be the name of +service to be advertised. The value of the key must be a boolean. If the +boolean is true, the port is recycled, thus leaving clients to remain oblivious +to the demand nature of job. If the value is set to false, clients receive port +death notifications when the job lets go of the receive right. The port will be +recreated atomically with respect to bootstrap_look_up() calls, so that clients +can trust that after receiving a port death notification, the new port will +have already been recreated. Setting the value to false should be done with +care. Not all clients may be able to handle this behavior. Finally, for the job +itself, the values will be replaced with Mach ports at the time of check-in +with +.Nm launchd . .It Sy Sockets This optional key is used to specify launch on demand sockets that can be used to let .Nm launchd -know when to run the job. The job can check-in and get a copy of the file descriptors using APIs outlined in +know when to run the job. The job must check-in to get a copy of the file descriptors using APIs outlined in .Xr launch 3 . The keys of the top level Sockets dictionary can be anything. They are meant for the application developer to use to differentiate different which descriptors correspond to which application level protocols (e.g. http vs. ftp vs. DNS...). @@ -264,8 +322,13 @@ This optional key implies SockFamily is set to "Unix". It specifies the path to or .Xr bind 2 to. +.It Sy SecureSocketWithKey +This optional key is a variant of SockPathName. Instead of binding to a known +path, a securely generated socket is created and the path is assigned to the +environment variable that is inherited by all jobs spawned by launchd. .It Sy SockPathMode -This optional key specifies the mode of the socket. Known bug: Property lists don't support octal, so please convert the value to decimal. +This optional key specifies the mode of the socket. Known bug: Property lists +don't support octal, so please convert the value to decimal. .It Sy Bonjour This optional key can be used to request that the service be registered with the .Xr mDNSResponder 8 . @@ -275,10 +338,18 @@ This optional key can be used to request that the datagram socket join a multica If the value is a hostname, then .Xr getaddrinfo 3 will be used to join the correct multicast address for a given socket family. -If an explicit IPv4 or IPv6 address is given, it is required that the SockFamily family also be set, otherwise the results are undefined. +If an explicit IPv4 or IPv6 address is given, it is required that the +SockFamily family also be set, otherwise the results are undefined. .El .El .Pp +.Sh DEPENDENCIES +Unlike many bootstrapping daemons, launchd has no explicit dependency model. +Interdependencies are expected to be solved through the use of IPC. +It is therefore in the best interest of a job developer who expects dependents +to define all of the sockets in the configuration file. This has the added +benefit of making it possible to start the job based on demand instead of +immediately. .Sh EXAMPLE XML PROPERTY LISTS .Pp The following XML Property List simply keeps "exampled" running continuously: @@ -293,8 +364,8 @@ The following XML Property List simply keeps "exampled" running continuously: .Dl .Dl exampled .Dl -.Dl OnDemand -.Dl +.Dl KeepAlive +.Dl .Dl .Dl .Pp @@ -314,4 +385,5 @@ Mac OS X System wide daemons. .Sh SEE ALSO .Xr launchctl 1 , .Xr launch 3 , -.Xr launchd 8 +.Xr launchd 8 , +.Xr plist 5 diff --git a/launchd/src/launchd_core_logic.c b/launchd/src/launchd_core_logic.c new file mode 100644 index 0000000..2c37ee9 --- /dev/null +++ b/launchd/src/launchd_core_logic.c @@ -0,0 +1,3059 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_APACHE_LICENSE_HEADER_START@ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @APPLE_APACHE_LICENSE_HEADER_END@ + */ + +static const char *const __rcs_file_version__ = "$Revision: 1.77 $"; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "launch.h" +#include "launch_priv.h" +#include "launchd.h" +#include "launchd_core_logic.h" +#include "launchd_unix_ipc.h" +#include "bootstrap_private.h" +#include "bootstrap.h" +#include "bootstrapServer.h" +#include "mpm_reply.h" + +/* sys/queue.h is not up to date */ +#ifndef SLIST_FOREACH_SAFE +#define SLIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = SLIST_FIRST((head)); \ + (var) && ((tvar) = SLIST_NEXT((var), field), 1); \ + (var) = (tvar)) +#endif + +struct machservice { + SLIST_ENTRY(machservice) sle; + struct jobcb *job; + mach_port_name_t port; + unsigned int isActive:1, reset:1, recv:1, hide:1, kUNCServer:1, __junk:27; + char name[0]; +}; + +static void machservice_setup(launch_data_t obj, const char *key, void *context); +static void machservice_setup_options(launch_data_t obj, const char *key, void *context); +static void machservice_resetport(struct jobcb *j, struct machservice *ms); + + +struct socketgroup { + SLIST_ENTRY(socketgroup) sle; + int *fds; + unsigned int junkfds:1, fd_cnt:31; + char name[0]; +}; + +static bool socketgroup_new(struct jobcb *j, const char *name, int *fds, unsigned int fd_cnt, bool junkfds); +static void socketgroup_delete(struct jobcb *j, struct socketgroup *sg); +static void socketgroup_watch(struct jobcb *j, struct socketgroup *sg); +static void socketgroup_ignore(struct jobcb *j, struct socketgroup *sg); +static void socketgroup_callback(struct jobcb *j, struct kevent *kev); +static void socketgroup_setup(launch_data_t obj, const char *key, void *context); + +struct watchpath { + SLIST_ENTRY(watchpath) sle; + int fd; + unsigned int is_qdir:1, __junk:31; + char name[0]; +}; + +static bool watchpath_new(struct jobcb *j, const char *name, bool qdir); +static void watchpath_delete(struct jobcb *j, struct watchpath *wp); +static void watchpath_watch(struct jobcb *j, struct watchpath *wp); +static void watchpath_ignore(struct jobcb *j, struct watchpath *wp); +static void watchpath_callback(struct jobcb *j, struct kevent *kev); + +struct calendarinterval { + SLIST_ENTRY(calendarinterval) sle; + struct tm when; +}; + +static bool calendarinterval_new(struct jobcb *j, struct tm *w); +static bool calendarinterval_new_from_obj(struct jobcb *j, launch_data_t obj); +static void calendarinterval_delete(struct jobcb *j, struct calendarinterval *ci); +static void calendarinterval_setalarm(struct jobcb *j, struct calendarinterval *ci); +static void calendarinterval_callback(struct jobcb *j, struct kevent *kev); + +struct envitem { + SLIST_ENTRY(envitem) sle; + char *value; + char key[0]; +}; + +static bool envitem_new(struct jobcb *j, const char *k, const char *v, bool global); +static void envitem_delete(struct jobcb *j, struct envitem *ei, bool global); +static void envitem_setup(launch_data_t obj, const char *key, void *context); + +struct limititem { + SLIST_ENTRY(limititem) sle; + struct rlimit lim; + unsigned int setsoft:1, sethard:1, which:30; +}; + +static bool limititem_update(struct jobcb *j, int w, rlim_t r); +static void limititem_delete(struct jobcb *j, struct limititem *li); +static void limititem_setup(launch_data_t obj, const char *key, void *context); + +typedef enum { + NETWORK_UP = 1, + NETWORK_DOWN, + SUCCESSFUL_EXIT, + FAILED_EXIT, + PATH_EXISTS, + PATH_MISSING, + // FILESYSTEMTYPE_IS_MOUNTED, /* for nfsiod, but maybe others */ +} semaphore_reason_t; + +struct semaphoreitem { + SLIST_ENTRY(semaphoreitem) sle; + semaphore_reason_t why; + char what[0]; +}; + +static bool semaphoreitem_new(struct jobcb *j, semaphore_reason_t why, const char *what); +static void semaphoreitem_delete(struct jobcb *j, struct semaphoreitem *si); +static void semaphoreitem_setup(launch_data_t obj, const char *key, void *context); +static void semaphoreitem_setup_paths(launch_data_t obj, const char *key, void *context); + + +struct jobcb { + kq_callback kqjob_callback; + SLIST_ENTRY(jobcb) sle; + SLIST_HEAD(, socketgroup) sockets; + SLIST_HEAD(, watchpath) vnodes; + SLIST_HEAD(, calendarinterval) cal_intervals; + SLIST_HEAD(, envitem) global_env; + SLIST_HEAD(, envitem) env; + SLIST_HEAD(, limititem) limits; + SLIST_HEAD(, machservice) machservices; + SLIST_HEAD(, semaphoreitem) semaphores; + SLIST_HEAD(, jobcb) jobs; + struct rusage ru; + struct jobcb *parent; + mach_port_t bs_port; + mach_port_t req_port; + mach_port_t wait_reply_port; + uid_t mach_uid; + char **argv; + char *prog; + char *rootdir; + char *workingdir; + char *username; + char *groupname; + char *stdinpath; + char *stdoutpath; + char *stderrpath; + pid_t p; + int argc; + int last_exit_status; + int execfd; + int nice; + int timeout; + time_t start_time; + size_t failed_exits; + unsigned int start_interval; + unsigned int checkedin:1, firstborn:1, debug:1, throttle:1, inetcompat:1, inetcompat_wait:1, + ondemand:1, session_create:1, low_pri_io:1, init_groups:1, priv_port_has_senders:1, + importing_global_env:1, importing_hard_limits:1, setmask:1, legacy_mach_job:1, runatload:1; + mode_t mask; + unsigned int globargv:1, wait4debugger:1, transfer_bstrap:1, unload_at_exit:1, force_ppc:1, stall_before_exec:1, __pad:26; + char label[0]; +}; + +static struct jobcb *job_import2(launch_data_t pload); +static void job_import_keys(launch_data_t obj, const char *key, void *context); +static void job_import_bool(struct jobcb *j, const char *key, bool value); +static void job_import_string(struct jobcb *j, const char *key, const char *value); +static void job_import_integer(struct jobcb *j, const char *key, long long value); +static void job_import_dictionary(struct jobcb *j, const char *key, launch_data_t value); +static void job_import_array(struct jobcb *j, const char *key, launch_data_t value); +static void job_watch(struct jobcb *j); +static void job_ignore(struct jobcb *j); +static void job_reap(struct jobcb *j); +static bool job_useless(struct jobcb *j); +static bool job_keepalive(struct jobcb *j); +static void job_start_child(struct jobcb *j, int execfd) __attribute__((noreturn)); +static void job_setup_attributes(struct jobcb *j); +static bool job_setup_machport(struct jobcb *j); +static void job_callback(void *obj, struct kevent *kev); +static pid_t job_fork(struct jobcb *j); +static size_t job_prep_log_preface(struct jobcb *j, char *buf); +static void job_setup_env_from_other_jobs(struct jobcb *j); +static void job_export_all2(struct jobcb *j, launch_data_t where); +static launch_data_t job_export2(struct jobcb *j, bool subjobs); + + +static const struct { + const char *key; + int val; +} launchd_keys2limits[] = { + { LAUNCH_JOBKEY_RESOURCELIMIT_CORE, RLIMIT_CORE }, + { LAUNCH_JOBKEY_RESOURCELIMIT_CPU, RLIMIT_CPU }, + { LAUNCH_JOBKEY_RESOURCELIMIT_DATA, RLIMIT_DATA }, + { LAUNCH_JOBKEY_RESOURCELIMIT_FSIZE, RLIMIT_FSIZE }, + { LAUNCH_JOBKEY_RESOURCELIMIT_MEMLOCK, RLIMIT_MEMLOCK }, + { LAUNCH_JOBKEY_RESOURCELIMIT_NOFILE, RLIMIT_NOFILE }, + { LAUNCH_JOBKEY_RESOURCELIMIT_NPROC, RLIMIT_NPROC }, + { LAUNCH_JOBKEY_RESOURCELIMIT_RSS, RLIMIT_RSS }, + { LAUNCH_JOBKEY_RESOURCELIMIT_STACK, RLIMIT_STACK }, +}; + +static time_t cronemu(int mon, int mday, int hour, int min); +static time_t cronemu_wday(int wday, int hour, int min); +static bool cronemu_mon(struct tm *wtm, int mon, int mday, int hour, int min); +static bool cronemu_mday(struct tm *wtm, int mday, int hour, int min); +static bool cronemu_hour(struct tm *wtm, int hour, int min); +static bool cronemu_min(struct tm *wtm, int min); + +static void simple_zombie_reaper(void *, struct kevent *); + +kq_callback kqsimple_zombie_reaper = simple_zombie_reaper; + +static int dir_has_files(const char *path); +static char **mach_cmd2argv(const char *string); +struct jobcb *root_job = NULL; +struct jobcb *gc_this_job = NULL; +size_t total_children = 0; + +void +simple_zombie_reaper(void *obj __attribute__((unused)), struct kevent *kev) +{ + waitpid(kev->ident, NULL, 0); +} + +void +job_ignore(struct jobcb *j) +{ + struct socketgroup *sg; + struct machservice *ms; + struct watchpath *wp; + + SLIST_FOREACH(sg, &j->sockets, sle) + socketgroup_ignore(j, sg); + + SLIST_FOREACH(wp, &j->vnodes, sle) + watchpath_ignore(j, wp); + + SLIST_FOREACH(ms, &j->machservices, sle) + launchd_assumes(launchd_mport_request_callback(ms->port, NULL, false) == KERN_SUCCESS); +} + +void +job_watch(struct jobcb *j) +{ + struct socketgroup *sg; + struct machservice *ms; + struct watchpath *wp; + + SLIST_FOREACH(sg, &j->sockets, sle) + socketgroup_watch(j, sg); + + SLIST_FOREACH(wp, &j->vnodes, sle) + watchpath_watch(j, wp); + + SLIST_FOREACH(ms, &j->machservices, sle) + launchd_assumes(launchd_mport_request_callback(ms->port, j, false) == KERN_SUCCESS); +} + +void +job_stop(struct jobcb *j) +{ + if (j->p) + kill(j->p, SIGTERM); +} + +launch_data_t +job_export(struct jobcb *j) +{ + return job_export2(j, true); +} + +launch_data_t +job_export2(struct jobcb *j, bool subjobs) +{ + launch_data_t tmp, tmp2, tmp3, r = launch_data_alloc(LAUNCH_DATA_DICTIONARY); + + if (r == NULL) + return NULL; + + if ((tmp = launch_data_new_string(j->label))) + launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_LABEL); + + if ((tmp = launch_data_new_bool(j->ondemand))) + launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_ONDEMAND); + + if ((tmp = launch_data_new_integer(j->last_exit_status))) + launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_LASTEXITSTATUS); + + if (j->p && (tmp = launch_data_new_integer(j->p))) + launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_PID); + + if ((tmp = launch_data_new_integer(j->timeout))) + launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_TIMEOUT); + + if (j->prog && (tmp = launch_data_new_string(j->prog))) + launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_PROGRAM); + + if (j->stdoutpath && (tmp = launch_data_new_string(j->stdoutpath))) + launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_STANDARDOUTPATH); + + if (j->stderrpath && (tmp = launch_data_new_string(j->stderrpath))) + launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_STANDARDERRORPATH); + + if (j->argv && (tmp = launch_data_alloc(LAUNCH_DATA_ARRAY))) { + int i; + + for (i = 0; i < j->argc; i++) { + if ((tmp2 = launch_data_new_string(j->argv[i]))) + launch_data_array_set_index(tmp, tmp2, i); + } + + launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_PROGRAMARGUMENTS); + } + + if (j->inetcompat && (tmp = launch_data_alloc(LAUNCH_DATA_DICTIONARY))) { + if ((tmp2 = launch_data_new_bool(j->inetcompat_wait))) + launch_data_dict_insert(tmp, tmp2, LAUNCH_JOBINETDCOMPATIBILITY_WAIT); + launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_INETDCOMPATIBILITY); + } + + if (!SLIST_EMPTY(&j->sockets) && (tmp = launch_data_alloc(LAUNCH_DATA_DICTIONARY))) { + struct socketgroup *sg; + int i; + + SLIST_FOREACH(sg, &j->sockets, sle) { + if ((tmp2 = launch_data_alloc(LAUNCH_DATA_ARRAY))) { + for (i = 0; i < sg->fd_cnt; i++) { + if ((tmp3 = launch_data_new_fd(sg->fds[i]))) + launch_data_array_set_index(tmp2, tmp3, i); + } + launch_data_dict_insert(tmp, tmp2, sg->name); + } + } + + launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_SOCKETS); + } + + if (!SLIST_EMPTY(&j->machservices) && (tmp = launch_data_alloc(LAUNCH_DATA_DICTIONARY))) { + struct machservice *ms; + + SLIST_FOREACH(ms, &j->machservices, sle) { + tmp2 = launch_data_new_machport(MACH_PORT_NULL); + launch_data_dict_insert(tmp, tmp2, ms->name); + } + + launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_MACHSERVICES); + } + + if (subjobs && !SLIST_EMPTY(&j->jobs) && (tmp = launch_data_alloc(LAUNCH_DATA_ARRAY))) { + struct jobcb *ji; + size_t i = 0; + + SLIST_FOREACH(ji, &j->jobs, sle) { + tmp2 = job_export2(ji, true); + launch_data_array_set_index(tmp, tmp2, i); + i++; + } + + launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_SUBJOBS); + } + + return r; +} + +void +job_remove_all_inactive(struct jobcb *j) +{ + struct jobcb *ji; + + SLIST_FOREACH(ji, &j->jobs, sle) + job_remove_all_inactive(ji); + + if (!job_active(j)) { + job_remove(j); + } else if (getpid() != 1) { + job_stop(j); + } +} + +void +job_remove(struct jobcb *j) +{ + struct jobcb *ji; + struct calendarinterval *ci; + struct socketgroup *sg; + struct watchpath *wp; + struct limititem *li; + struct envitem *ei; + struct machservice *ms; + struct semaphoreitem *si; + + job_log(j, LOG_DEBUG, "Removed"); + + if (j->p) { + if (kevent_mod(j->p, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, &kqsimple_zombie_reaper) == -1) { + job_reap(j); + } else { + /* we've attached the simple zombie reaper, we're going to delete the job before it is dead */ + total_children--; + job_stop(j); + } + } + + if (j->parent) + SLIST_REMOVE(&j->parent->jobs, j, jobcb, sle); + + if (j->execfd) + launchd_assumes(close(j->execfd) == 0); + + if (j->bs_port) { + if (j->transfer_bstrap) { + launchd_assumes(launchd_mport_deallocate(j->bs_port) == KERN_SUCCESS); + } else { + launchd_assumes(launchd_mport_close_recv(j->bs_port) == KERN_SUCCESS); + } + } + + if (j->req_port) + launchd_assumes(launchd_mport_deallocate(j->req_port) == KERN_SUCCESS); + +#if 0 + if (j->wait_reply_port) { + } +#endif + + while ((ji = SLIST_FIRST(&j->jobs))) + job_remove(ji); + + while ((sg = SLIST_FIRST(&j->sockets))) + socketgroup_delete(j, sg); + + while ((wp = SLIST_FIRST(&j->vnodes))) + watchpath_delete(j, wp); + + while ((ci = SLIST_FIRST(&j->cal_intervals))) + calendarinterval_delete(j, ci); + + while ((ei = SLIST_FIRST(&j->env))) + envitem_delete(j, ei, false); + + while ((ei = SLIST_FIRST(&j->global_env))) + envitem_delete(j, ei, true); + + while ((li = SLIST_FIRST(&j->limits))) + limititem_delete(j, li); + + while ((ms = SLIST_FIRST(&j->machservices))) + machservice_delete(ms); + + while ((si = SLIST_FIRST(&j->semaphores))) + semaphoreitem_delete(j, si); + + if (j->prog) + free(j->prog); + + if (j->argv) + free(j->argv); + + if (j->rootdir) + free(j->rootdir); + + if (j->workingdir) + free(j->workingdir); + + if (j->username) + free(j->username); + + if (j->groupname) + free(j->groupname); + + if (j->stdinpath) + free(j->stdinpath); + + if (j->stdoutpath) + free(j->stdoutpath); + + if (j->stderrpath) + free(j->stderrpath); + + if (j->start_interval) + kevent_mod((uintptr_t)&j->start_interval, EVFILT_TIMER, EV_DELETE, 0, 0, NULL); + + kevent_mod((uintptr_t)j, EVFILT_TIMER, EV_DELETE, 0, 0, NULL); + free(j); +} + +void +socketgroup_setup(launch_data_t obj, const char *key, void *context) +{ + launch_data_t tmp_oai; + struct jobcb *j = context; + unsigned int i, fd_cnt = 1; + int *fds; + + if (launch_data_get_type(obj) == LAUNCH_DATA_ARRAY) + fd_cnt = launch_data_array_get_count(obj); + + fds = alloca(fd_cnt * sizeof(int)); + + for (i = 0; i < fd_cnt; i++) { + if (launch_data_get_type(obj) == LAUNCH_DATA_ARRAY) + tmp_oai = launch_data_array_get_index(obj, i); + else + tmp_oai = obj; + + fds[i] = launch_data_get_fd(tmp_oai); + } + + socketgroup_new(j, key, fds, fd_cnt, strcmp(key, LAUNCH_JOBKEY_BONJOURFDS) == 0); + + ipc_revoke_fds(obj); +} + +bool +job_setup_machport(struct jobcb *j) +{ + if (!launchd_assumes(launchd_mport_create_recv(&j->bs_port) == KERN_SUCCESS)) + goto out_bad; + + if (!launchd_assumes(launchd_mport_request_callback(j->bs_port, j, true) == KERN_SUCCESS)) + goto out_bad2; + + return true; +out_bad2: + launchd_assumes(launchd_mport_close_recv(j->bs_port) == KERN_SUCCESS); +out_bad: + return false; +} + +struct jobcb * +job_new_via_mach_init(struct jobcb *jbs, const char *cmd, uid_t uid, bool ond) +{ + const char **argv = (const char **)mach_cmd2argv(cmd); + struct jobcb *j = NULL; + char buf[1000]; + + if (!launchd_assumes(argv != NULL)) + goto out_bad; + + /* preflight the string so we know how big it is */ + sprintf(buf, "100000.%s", basename((char *)argv[0])); + + j = job_new(jbs, buf, NULL, argv, NULL, MACH_PORT_NULL); + + free(argv); + + if (!launchd_assumes(j != NULL)) + goto out_bad; + + j->mach_uid = uid; + j->ondemand = ond; + j->legacy_mach_job = true; + j->priv_port_has_senders = true; /* the IPC that called us will make-send on this port */ + + if (!job_setup_machport(j)) + goto out_bad; + + if (!launchd_assumes(launchd_mport_notify_req(j->bs_port, MACH_NOTIFY_NO_SENDERS) == KERN_SUCCESS)) { + launchd_assumes(launchd_mport_close_recv(j->bs_port) == KERN_SUCCESS); + goto out_bad; + } + + sprintf(j->label, "%d.%s", MACH_PORT_INDEX(j->bs_port), basename(j->argv[0])); + + job_log(j, LOG_INFO, "New%s server in bootstrap: %x", ond ? " on-demand" : "", jbs->bs_port); + + return j; + +out_bad: + if (j) + job_remove(j); + return NULL; +} + +kern_return_t +job_handle_mpm_wait(struct jobcb *j, mach_port_t srp, int *waitstatus) +{ + if (j->p) { + j->wait_reply_port = srp; + return MIG_NO_REPLY; + } + + *waitstatus = j->last_exit_status; + + return 0; +} + +struct jobcb * +job_new_spawn(const char *label, const char *path, const char *workingdir, const char *const *argv, const char *const *env, mode_t *u_mask, bool w4d, bool fppc) +{ + struct jobcb *jr; + + if ((jr = job_find(root_job, label)) != NULL) { + errno = EEXIST; + return NULL; + } + + jr = job_new(root_job, label, path, argv, NULL, MACH_PORT_NULL); + + if (!jr) + return NULL; + + jr->unload_at_exit = true; + jr->stall_before_exec = w4d; + jr->force_ppc = fppc; + + if (!job_setup_machport(jr)) { + job_remove(jr); + return NULL; + } + + if (workingdir) + jr->workingdir = strdup(workingdir); + + if (u_mask) { + jr->mask = *u_mask; + jr->setmask = true; + } + + if (env) for (; *env; env++) { + char newkey[strlen(*env) + 1], *eqoff = strchr(*env, '='); + if (!eqoff) { + job_log(jr, LOG_WARNING, "Environmental variable missing '=' separator: %s", *env); + continue; + } + strcpy(newkey, *env); + *eqoff = '\0'; + envitem_new(jr, newkey, eqoff + 1, false); + } + + job_start(jr); + + return jr; +} + +struct jobcb * +job_new(struct jobcb *p, const char *label, const char *prog, const char *const *argv, const char *stdinpath, mach_port_t reqport) +{ + const char *const *argv_tmp = argv; + char *co; + int i, cc = 0; + struct jobcb *j; + + if (reqport == MACH_PORT_NULL && prog == NULL && argv == NULL) { + errno = EINVAL; + return NULL; + } + + j = calloc(1, sizeof(struct jobcb) + strlen(label) + 1); + + if (!launchd_assumes(j != NULL)) + goto out_bad; + + strcpy(j->label, label); + j->kqjob_callback = job_callback; + j->parent = p ? job_get_bs(p) : NULL; + j->ondemand = true; + j->checkedin = true; + j->firstborn = (strcmp(label, FIRSTBORN_LABEL) == 0); + + if (reqport != MACH_PORT_NULL) { + j->req_port = reqport; + if (!launchd_assumes(launchd_mport_notify_req(reqport, MACH_NOTIFY_DEAD_NAME) == KERN_SUCCESS)) + goto out_bad; + } + + if (prog) { + j->prog = strdup(prog); + if (!launchd_assumes(j->prog != NULL)) + goto out_bad; + } + + if (stdinpath) { + j->stdinpath = strdup(stdinpath); + if (!launchd_assumes(j->stdinpath != NULL)) + goto out_bad; + } + + if (argv) { + while (*argv_tmp++) + j->argc++; + + for (i = 0; i < j->argc; i++) + cc += strlen(argv[i]) + 1; + + j->argv = malloc((j->argc + 1) * sizeof(char *) + cc); + + if (!launchd_assumes(j != NULL)) + goto out_bad; + + co = ((char *)j->argv) + ((j->argc + 1) * sizeof(char *)); + + for (i = 0; i < j->argc; i++) { + j->argv[i] = co; + strcpy(co, argv[i]); + co += strlen(argv[i]) + 1; + } + j->argv[i] = NULL; + } + + if (j->parent) { + SLIST_INSERT_HEAD(&j->parent->jobs, j, sle); + job_log(j->parent, LOG_DEBUG, "Conceived"); + } + + return j; + +out_bad: + if (j) { + if (j->prog) + free(j->prog); + if (j->stdinpath) + free(j->stdinpath); + free(j); + } + return NULL; +} + +struct jobcb * +job_import(launch_data_t pload) +{ + struct jobcb *j = job_import2(pload); + + if (j == NULL) + return NULL; + + job_dispatch(j); + + return j; +} + +launch_data_t +job_import_bulk(launch_data_t pload) +{ + launch_data_t resp = launch_data_alloc(LAUNCH_DATA_ARRAY); + struct jobcb **ja; + size_t i, c = launch_data_array_get_count(pload); + + ja = alloca(c * sizeof(struct jobcb *)); + + for (i = 0; i < c; i++) { + if ((ja[i] = job_import2(launch_data_array_get_index(pload, i)))) + errno = 0; + launch_data_array_set_index(resp, launch_data_new_errno(errno), i); + } + + for (i = 0; i < c; i++) { + if (ja[i] == NULL) + continue; + job_dispatch(ja[i]); + } + + return resp; +} + +void +job_import_bool(struct jobcb *j, const char *key, bool value) +{ + switch (key[0]) { + case 'f': + case 'F': + if (strcasecmp(key, LAUNCH_JOBKEY_FORCEPOWERPC) == 0) + j->force_ppc = value; + break; + case 'k': + case 'K': + if (strcasecmp(key, LAUNCH_JOBKEY_KEEPALIVE) == 0) + j->ondemand = !value; + break; + case 'o': + case 'O': + if (strcasecmp(key, LAUNCH_JOBKEY_ONDEMAND) == 0) + j->ondemand = value; + break; + case 'd': + case 'D': + if (strcasecmp(key, LAUNCH_JOBKEY_DEBUG) == 0) + j->debug = value; + break; + case 's': + case 'S': + if (strcasecmp(key, LAUNCH_JOBKEY_SESSIONCREATE) == 0) + j->session_create = value; + break; + case 'l': + case 'L': + if (strcasecmp(key, LAUNCH_JOBKEY_LOWPRIORITYIO) == 0) + j->low_pri_io = value; + break; + case 'i': + case 'I': + if (strcasecmp(key, LAUNCH_JOBKEY_INITGROUPS) == 0) + j->init_groups = value; + break; + case 'r': + case 'R': + if (strcasecmp(key, LAUNCH_JOBKEY_RUNATLOAD) == 0) + j->runatload = value; + break; + case 'e': + case 'E': + if (strcasecmp(key, LAUNCH_JOBKEY_ENABLEGLOBBING) == 0) + j->globargv = value; + break; + case 'w': + case 'W': + if (strcasecmp(key, LAUNCH_JOBKEY_WAITFORDEBUGGER) == 0) + j->wait4debugger = value; + break; + default: + break; + } +} + +void +job_import_string(struct jobcb *j, const char *key, const char *value) +{ + char **where2put = NULL; + char **ignore = (char **)-1; + + switch (key[0]) { + case 'p': + case 'P': + if (strcasecmp(key, LAUNCH_JOBKEY_PROGRAM) == 0) + where2put = ignore; + break; + case 'l': + case 'L': + if (strcasecmp(key, LAUNCH_JOBKEY_LABEL) == 0) + where2put = ignore; + break; + case 'r': + case 'R': + if (strcasecmp(key, LAUNCH_JOBKEY_ROOTDIRECTORY) == 0) + where2put = &j->rootdir; + break; + case 'w': + case 'W': + if (strcasecmp(key, LAUNCH_JOBKEY_WORKINGDIRECTORY) == 0) + where2put = &j->workingdir; + break; + case 'u': + case 'U': + if (strcasecmp(key, LAUNCH_JOBKEY_USERNAME) == 0) + where2put = &j->username; + break; + case 'g': + case 'G': + if (strcasecmp(key, LAUNCH_JOBKEY_GROUPNAME) == 0) + where2put = &j->groupname; + break; + case 's': + case 'S': + if (strcasecmp(key, LAUNCH_JOBKEY_STANDARDOUTPATH) == 0) { + where2put = &j->stdoutpath; + } else if (strcasecmp(key, LAUNCH_JOBKEY_STANDARDERRORPATH) == 0) { + where2put = &j->stderrpath; + } + break; + default: + break; + } + + if (where2put) { + if (where2put == ignore) + return; + + launchd_assumes((*where2put = strdup(value)) != NULL); + } else { + job_log(j, LOG_WARNING, "Unknown value for key %s: %s", key, value); + } +} + +void +job_import_integer(struct jobcb *j, const char *key, long long value) +{ + switch (key[0]) { + case 'n': + case 'N': + if (strcasecmp(key, LAUNCH_JOBKEY_NICE) == 0) + j->nice = value; + break; + case 't': + case 'T': + if (strcasecmp(key, LAUNCH_JOBKEY_TIMEOUT) == 0) { + if (value <= 0) + job_log(j, LOG_WARNING, "Timeout less than or equal to zero. Ignoring."); + else + j->timeout = value; + } + break; + case 'u': + case 'U': + if (strcasecmp(key, LAUNCH_JOBKEY_UMASK) == 0) { + j->mask = value; + j->setmask = true; + } + break; + case 's': + case 'S': + if (strcasecmp(key, LAUNCH_JOBKEY_STARTINTERVAL) == 0) { + if (value <= 0) + job_log(j, LOG_WARNING, "StartInterval is not greater than zero, ignoring"); + else + j->start_interval = value; + if (-1 == kevent_mod((uintptr_t)&j->start_interval, EVFILT_TIMER, EV_ADD, NOTE_SECONDS, value, j)) + job_log_error(j, LOG_ERR, "adding kevent timer"); + } + break; + default: + break; + } +} + +void +job_import_dictionary(struct jobcb *j, const char *key, launch_data_t value) +{ + launch_data_t tmp; + + switch (key[0]) { + case 'k': + case 'K': + if (strcasecmp(key, LAUNCH_JOBKEY_KEEPALIVE) == 0) + launch_data_dict_iterate(value, semaphoreitem_setup, j); + break; + case 'i': + case 'I': + if (strcasecmp(key, LAUNCH_JOBKEY_INETDCOMPATIBILITY) == 0) { + j->inetcompat = true; + if ((tmp = launch_data_dict_lookup(value, LAUNCH_JOBINETDCOMPATIBILITY_WAIT))) + j->inetcompat_wait = launch_data_get_bool(tmp); + } + break; + case 'e': + case 'E': + if (strcasecmp(key, LAUNCH_JOBKEY_ENVIRONMENTVARIABLES) == 0) + launch_data_dict_iterate(value, envitem_setup, j); + break; + case 'u': + case 'U': + if (strcasecmp(key, LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES) == 0) { + j->importing_global_env = true; + launch_data_dict_iterate(value, envitem_setup, j); + j->importing_global_env = false; + } + break; + case 's': + case 'S': + if (strcasecmp(key, LAUNCH_JOBKEY_SOCKETS) == 0) { + launch_data_dict_iterate(value, socketgroup_setup, j); + } else if (strcasecmp(key, LAUNCH_JOBKEY_STARTCALENDARINTERVAL) == 0) { + calendarinterval_new_from_obj(j, value); + } else if (strcasecmp(key, LAUNCH_JOBKEY_SOFTRESOURCELIMITS) == 0) { + launch_data_dict_iterate(value, limititem_setup, j); + } + break; + case 'h': + case 'H': + if (strcasecmp(key, LAUNCH_JOBKEY_HARDRESOURCELIMITS) == 0) { + j->importing_hard_limits = true; + launch_data_dict_iterate(value, limititem_setup, j); + j->importing_hard_limits = false; + } + break; + case 'm': + case 'M': + if (strcasecmp(key, LAUNCH_JOBKEY_MACHSERVICES) == 0) { + launch_data_dict_iterate(value, machservice_setup, j); + if (!SLIST_EMPTY(&j->machservices)) + job_setup_machport(j); + } + break; + default: + break; + } +} + +void +job_import_array(struct jobcb *j, const char *key, launch_data_t value) +{ + bool is_q_dir = false; + bool is_wp = false; + + switch (key[0]) { + case 'q': + case 'Q': + if (strcasecmp(key, LAUNCH_JOBKEY_QUEUEDIRECTORIES) == 0) { + is_q_dir = true; + is_wp = true; + } + break; + case 'w': + case 'W': + if (strcasecmp(key, LAUNCH_JOBKEY_WATCHPATHS) == 0) + is_wp = true; + break; + case 'b': + case 'B': + if (strcasecmp(key, LAUNCH_JOBKEY_BONJOURFDS) == 0) + socketgroup_setup(value, LAUNCH_JOBKEY_BONJOURFDS, j); + break; + case 's': + case 'S': + if (strcasecmp(key, LAUNCH_JOBKEY_STARTCALENDARINTERVAL) == 0) { + size_t i = 0, ci_cnt = launch_data_array_get_count(value); + for (i = 0; i < ci_cnt; i++) + calendarinterval_new_from_obj(j, launch_data_array_get_index(value, i)); + } + break; + default: + break; + } + + if (is_wp) { + size_t i, wp_cnt = launch_data_array_get_count(value); + const char *thepath; + for (i = 0; i < wp_cnt; i++) { + thepath = launch_data_get_string(launch_data_array_get_index(value, i)); + watchpath_new(j, thepath, is_q_dir); + } + } +} + +void +job_import_keys(launch_data_t obj, const char *key, void *context) +{ + struct jobcb *j = context; + launch_data_type_t kind; + + if (obj == NULL) + return; + + kind = launch_data_get_type(obj); + + switch (kind) { + case LAUNCH_DATA_BOOL: + job_import_bool(j, key, launch_data_get_bool(obj)); + break; + case LAUNCH_DATA_STRING: + job_import_string(j, key, launch_data_get_string(obj)); + break; + case LAUNCH_DATA_INTEGER: + job_import_integer(j, key, launch_data_get_integer(obj)); + break; + case LAUNCH_DATA_DICTIONARY: + job_import_dictionary(j, key, obj); + break; + case LAUNCH_DATA_ARRAY: + job_import_array(j, key, obj); + break; + default: + job_log(j, LOG_WARNING, "Unknown value type '%d' for key: %s", kind, key); + break; + } +} + +struct jobcb * +job_import2(launch_data_t pload) +{ + launch_data_t tmp, ldpa; + const char *label = NULL, *prog = NULL; + const char **argv = NULL; + struct jobcb *j; + + if (pload == NULL) + return NULL; + + if (launch_data_get_type(pload) != LAUNCH_DATA_DICTIONARY) + return NULL; + + if ((tmp = launch_data_dict_lookup(pload, LAUNCH_JOBKEY_LABEL)) && + (launch_data_get_type(tmp) == LAUNCH_DATA_STRING)) { + label = launch_data_get_string(tmp); + } + if ((tmp = launch_data_dict_lookup(pload, LAUNCH_JOBKEY_PROGRAM)) && + (launch_data_get_type(tmp) == LAUNCH_DATA_STRING)) { + prog = launch_data_get_string(tmp); + } + ldpa = launch_data_dict_lookup(pload, LAUNCH_JOBKEY_PROGRAMARGUMENTS); + + if (label == NULL) { + errno = EINVAL; + return NULL; + } else if ((j = job_find(root_job, label)) != NULL) { + errno = EEXIST; + return NULL; + } else if (label[0] == '\0' || (strncasecmp(label, "", strlen("com.apple.launchd")) == 0) || + (strtol(label, NULL, 10) != 0)) { + syslog(LOG_ERR, "Somebody attempted to use a reserved prefix for a label: %s", label); + /* the empty string, com.apple.launchd and number prefixes for labels are reserved */ + errno = EINVAL; + return NULL; + } + + if (ldpa) { + size_t i, c = launch_data_array_get_count(ldpa); + + argv = alloca((c + 1) * sizeof(char *)); + + for (i = 0; i < c; i++) + argv[i] = launch_data_get_string(launch_data_array_get_index(ldpa, i)); + argv[i] = NULL; + } + + if ((j = job_new(root_job, label, prog, argv, NULL, MACH_PORT_NULL))) + launch_data_dict_iterate(pload, job_import_keys, j); + + return j; +} + +struct jobcb * +job_find(struct jobcb *j, const char *label) +{ + struct jobcb *jr, *ji; + + if (label[0] == '\0') + return root_job; + + if (strcmp(j->label, label) == 0) + return j; + + SLIST_FOREACH(ji, &j->jobs, sle) { + if ((jr = job_find(ji, label))) + return jr; + } + + errno = ESRCH; + return NULL; +} + +struct jobcb * +job_find_by_pid(struct jobcb *j, pid_t p) +{ + struct jobcb *jr, *ji; + + if (j->p == p) + return j; + + SLIST_FOREACH(ji, &j->jobs, sle) { + if ((jr = job_find_by_pid(ji, p))) + return jr; + } + + errno = ESRCH; + return NULL; +} + +void +job_export_all2(struct jobcb *j, launch_data_t where) +{ + launch_data_t tmp; + struct jobcb *ji; + + if (launchd_assumes((tmp = job_export2(j, false)) != NULL)) + launch_data_dict_insert(where, tmp, j->label); + + SLIST_FOREACH(ji, &j->jobs, sle) + job_export_all2(ji, where); +} + +launch_data_t +job_export_all(void) +{ + launch_data_t resp = launch_data_alloc(LAUNCH_DATA_DICTIONARY); + + job_export_all2(root_job, resp); + + return resp; +} + +void +job_reap(struct jobcb *j) +{ + struct rusage ru; + time_t td = time(NULL) - j->start_time; + bool bad_exit = false; + int status; + + job_log(j, LOG_DEBUG, "Reaping"); + + if (j->execfd) { + launchd_assumes(close(j->execfd) == 0); + j->execfd = 0; + } + + if (!launchd_assumes(wait4(j->p, &status, 0, &ru) != -1)) { + return; + } + + if (j->wait_reply_port) { + job_log(j, LOG_DEBUG, "MPM wait reply being sent"); + launchd_assumes(mpm_wait_reply(j->wait_reply_port, 0, status) == 0); + j->wait_reply_port = MACH_PORT_NULL; + } + + timeradd(&ru.ru_utime, &j->ru.ru_utime, &j->ru.ru_utime); + timeradd(&ru.ru_stime, &j->ru.ru_stime, &j->ru.ru_stime); + j->ru.ru_maxrss += ru.ru_maxrss; + j->ru.ru_ixrss += ru.ru_ixrss; + j->ru.ru_idrss += ru.ru_idrss; + j->ru.ru_isrss += ru.ru_isrss; + j->ru.ru_minflt += ru.ru_minflt; + j->ru.ru_majflt += ru.ru_majflt; + j->ru.ru_nswap += ru.ru_nswap; + j->ru.ru_inblock += ru.ru_inblock; + j->ru.ru_oublock += ru.ru_oublock; + j->ru.ru_msgsnd += ru.ru_msgsnd; + j->ru.ru_msgrcv += ru.ru_msgrcv; + j->ru.ru_nsignals += ru.ru_nsignals; + j->ru.ru_nvcsw += ru.ru_nvcsw; + j->ru.ru_nivcsw += ru.ru_nivcsw; + + if (WIFEXITED(status) && WEXITSTATUS(status) != 0) { + job_log(j, LOG_WARNING, "exited with exit code: %d", WEXITSTATUS(status)); + bad_exit = true; + } + + if (WIFSIGNALED(status)) { + int s = WTERMSIG(status); + if (SIGKILL == s || SIGTERM == s) { + job_log(j, LOG_NOTICE, "Exited: %s", strsignal(s)); + } else { + job_log(j, LOG_WARNING, "Exited abnormally: %s", strsignal(s)); + bad_exit = true; + } + } + + if (!j->ondemand && !j->legacy_mach_job) { + if (td < LAUNCHD_MIN_JOB_RUN_TIME) { + job_log(j, LOG_WARNING, "respawning too quickly! throttling"); + bad_exit = true; + j->throttle = true; + } else if (td >= LAUNCHD_REWARD_JOB_RUN_TIME) { + job_log(j, LOG_INFO, "lived long enough, forgiving past exit failures"); + j->failed_exits = 0; + } + } + + if (!j->legacy_mach_job && bad_exit) + j->failed_exits++; + + if (j->failed_exits > 0) { + int failures_left = LAUNCHD_FAILED_EXITS_THRESHOLD - j->failed_exits; + if (failures_left) + job_log(j, LOG_WARNING, "%d more failure%s without living at least %d seconds will cause job removal", + failures_left, failures_left > 1 ? "s" : "", LAUNCHD_REWARD_JOB_RUN_TIME); + } + + total_children--; + j->last_exit_status = status; + j->p = 0; +} + +void +job_dispatch(struct jobcb *j) +{ + if (job_active(j)) { + return; + } else if (job_useless(j)) { + job_remove(j); + } else if (job_keepalive(j)) { + job_start(j); + } else { + job_watch(j); + } +} + +void +job_callback(void *obj, struct kevent *kev) +{ + struct jobcb *j = obj; + bool d = j->debug; + int oldmask = 0; + + if (d) { + oldmask = setlogmask(LOG_UPTO(LOG_DEBUG)); + } + + switch (kev->filter) { + case EVFILT_PROC: + job_reap(j); + + if (j->firstborn) { + job_log(j, LOG_DEBUG, "first born died, begin shutdown"); + launchd_shutdown(); + } else { + job_dispatch(j); + } + break; + case EVFILT_TIMER: + if ((uintptr_t)j == kev->ident) { + job_start(j); + } else { + calendarinterval_callback(j, kev); + } + break; + case EVFILT_VNODE: + watchpath_callback(j, kev); + break; + case EVFILT_READ: + if ((int)kev->ident != j->execfd) { + socketgroup_callback(j, kev); + break; + } + if (j->wait4debugger) { + /* Allow somebody else to attach */ + launchd_assumes(kill(j->p, SIGSTOP) != -1); + launchd_assumes(ptrace(PT_DETACH, j->p, NULL, 0) != -1); + } + if (kev->data > 0) { + int e; + + read(j->execfd, &e, sizeof(e)); + errno = e; + job_log_error(j, LOG_ERR, "execve()"); + job_remove(j); + j = NULL; + } else { + launchd_assumes(close(j->execfd) == 0); + j->execfd = 0; + } + break; + case EVFILT_MACHPORT: + job_start(j); + break; + default: + launchd_assumes(false); + break; + } + + if (d) { + /* the job might have been removed, must not call job_log() */ + setlogmask(oldmask); + } +} + +void +job_start(struct jobcb *j) +{ + int spair[2]; + int execspair[2]; + char nbuf[64]; + pid_t c; + bool sipc = false; + + if (!launchd_assumes(j->req_port == MACH_PORT_NULL)) + return; + + if (!launchd_assumes(j->parent != NULL)) + return; + + if (job_active(j)) { + job_log(j, LOG_DEBUG, "Already started"); + return; + } else if (!j->legacy_mach_job && j->throttle) { + j->throttle = false; + job_log(j, LOG_WARNING, "Throttling: Will restart in %d seconds", LAUNCHD_MIN_JOB_RUN_TIME); + launchd_assumes(kevent_mod((uintptr_t)j, EVFILT_TIMER, EV_ADD|EV_ONESHOT, + NOTE_SECONDS, LAUNCHD_MIN_JOB_RUN_TIME, j) != -1); + return; + } + + job_log(j, LOG_DEBUG, "Starting"); + + if (!j->legacy_mach_job) + sipc = (!SLIST_EMPTY(&j->sockets) || !SLIST_EMPTY(&j->machservices)); + + /* FIXME, using stdinpath is a hack for re-reading the conf file */ + if (j->stdinpath) + sipc = true; + + j->checkedin = false; + + if (sipc) + socketpair(AF_UNIX, SOCK_STREAM, 0, spair); + + socketpair(AF_UNIX, SOCK_STREAM, 0, execspair); + + time(&j->start_time); + + if (j->bs_port) { + launchd_assumes(launchd_mport_notify_req(j->bs_port, MACH_NOTIFY_NO_SENDERS) == KERN_SUCCESS); + } + + switch (c = job_fork(j->bs_port ? j : j->parent)) { + case -1: + job_log_error(j, LOG_ERR, "fork() failed, will try again in one second"); + launchd_assumes(close(execspair[0]) == 0); + launchd_assumes(close(execspair[1]) == 0); + if (sipc) { + launchd_assumes(close(spair[0]) == 0); + launchd_assumes(close(spair[1]) == 0); + } + break; + case 0: + launchd_assumes(close(execspair[0]) == 0); + /* wait for our parent to say they've attached a kevent to us */ + read(_fd(execspair[1]), &c, sizeof(c)); + if (j->firstborn) { + setpgid(getpid(), getpid()); + if (isatty(STDIN_FILENO)) { + if (tcsetpgrp(STDIN_FILENO, getpid()) == -1) + job_log_error(j, LOG_WARNING, "tcsetpgrp()"); + } + } + + if (sipc) { + launchd_assumes(close(spair[0]) == 0); + sprintf(nbuf, "%d", spair[1]); + setenv(LAUNCHD_TRUSTED_FD_ENV, nbuf, 1); + } + job_start_child(j, execspair[1]); + break; + default: + if (!SLIST_EMPTY(&j->machservices)) + j->priv_port_has_senders = true; + j->p = c; + total_children++; + launchd_assumes(close(execspair[1]) == 0); + j->execfd = _fd(execspair[0]); + if (sipc) { + launchd_assumes(close(spair[1]) == 0); + ipc_open(_fd(spair[0]), j); + } + if (kevent_mod(j->execfd, EVFILT_READ, EV_ADD, 0, 0, &j->kqjob_callback) == -1) + job_log_error(j, LOG_ERR, "kevent_mod(j->execfd): %m"); + if (kevent_mod(c, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, &j->kqjob_callback) == -1) { + job_log_error(j, LOG_ERR, "kevent()"); + job_reap(j); + } else { + if (j->ondemand) + job_ignore(j); + } + + if (!j->stall_before_exec) { + /* this unblocks the child and avoids a race + * between the above fork() and the kevent_mod() */ + write(j->execfd, &c, sizeof(c)); + } + break; + } +} + +void +job_start_child(struct jobcb *j, int execfd) +{ + const char *file2exec = "/usr/libexec/launchproxy"; + const char **argv; + int gflags = GLOB_NOSORT|GLOB_NOCHECK|GLOB_TILDE|GLOB_DOOFFS; + glob_t g; + int i; + + job_setup_attributes(j); + + if (j->argv && j->globargv) { + g.gl_offs = 1; + for (i = 0; i < j->argc; i++) { + if (i > 0) + gflags |= GLOB_APPEND; + if (glob(j->argv[i], gflags, NULL, &g) != 0) { + job_log_error(j, LOG_ERR, "glob(\"%s\")", j->argv[i]); + exit(EXIT_FAILURE); + } + } + g.gl_pathv[0] = (char *)file2exec; + argv = (const char **)g.gl_pathv; + } else if (j->argv) { + argv = alloca((j->argc + 2) * sizeof(char *)); + argv[0] = file2exec; + for (i = 0; i < j->argc; i++) + argv[i + 1] = j->argv[i]; + argv[i + 1] = NULL; + } else { + argv = alloca(3 * sizeof(char *)); + argv[0] = file2exec; + argv[1] = j->prog; + argv[2] = NULL; + } + + if (!j->inetcompat) + argv++; + + if (j->wait4debugger && ptrace(PT_TRACE_ME, getpid(), NULL, 0) == -1) + job_log_error(j, LOG_ERR, "ptrace(PT_TRACE_ME, ...)"); + + if (j->force_ppc) { + int affinmib[] = { CTL_KERN, KERN_AFFINITY, 1, 1 }; + size_t mibsz = sizeof(affinmib) / sizeof(affinmib[0]); + + if (sysctl(affinmib, mibsz, NULL, NULL, NULL, 0) == -1) + job_log_error(j, LOG_WARNING, "Failed to force PowerPC execution"); + } + + if (j->prog) { + execv(j->inetcompat ? file2exec : j->prog, (char *const*)argv); + job_log_error(j, LOG_ERR, "execv(\"%s\", ...)", j->prog); + } else { + execvp(j->inetcompat ? file2exec : argv[0], (char *const*)argv); + job_log_error(j, LOG_ERR, "execvp(\"%s\", ...)", argv[0]); + } + + write(execfd, &errno, sizeof(errno)); + exit(EXIT_FAILURE); +} + +void job_setup_env_from_other_jobs(struct jobcb *j) +{ + struct envitem *ei; + struct jobcb *ji; + + SLIST_FOREACH(ji, &j->jobs, sle) + job_setup_env_from_other_jobs(ji); + + SLIST_FOREACH(ei, &j->global_env, sle) + setenv(ei->key, ei->value, 1); +} + +void +job_setup_attributes(struct jobcb *j) +{ + struct limititem *li; + struct envitem *ei; + struct group *gre = NULL; + gid_t gre_g = 0; + + setpriority(PRIO_PROCESS, 0, j->nice); + + SLIST_FOREACH(li, &j->limits, sle) { + struct rlimit rl; + + if (getrlimit(li->which, &rl) == -1) { + job_log_error(j, LOG_WARNING, "getrlimit()"); + continue; + } + + if (li->sethard) + rl.rlim_max = li->lim.rlim_max; + if (li->setsoft) + rl.rlim_cur = li->lim.rlim_cur; + + if (setrlimit(li->which, &rl) == -1) + job_log_error(j, LOG_WARNING, "setrlimit()"); + } + + if (!j->inetcompat && j->session_create) + launchd_SessionCreate(); + + if (j->low_pri_io) { + int lowprimib[] = { CTL_KERN, KERN_PROC_LOW_PRI_IO }; + int val = 1; + + if (sysctl(lowprimib, sizeof(lowprimib) / sizeof(lowprimib[0]), NULL, NULL, &val, sizeof(val)) == -1) + job_log_error(j, LOG_WARNING, "sysctl(\"%s\")", "kern.proc_low_pri_io"); + } + if (j->rootdir) { + chroot(j->rootdir); + chdir("."); + } + if (j->groupname) { + gre = getgrnam(j->groupname); + if (gre) { + gre_g = gre->gr_gid; + if (-1 == setgid(gre_g)) { + job_log_error(j, LOG_ERR, "setgid(%d)", gre_g); + exit(EXIT_FAILURE); + } + } else { + job_log(j, LOG_ERR, "getgrnam(\"%s\") failed", j->groupname); + exit(EXIT_FAILURE); + } + } + if (j->username || j->mach_uid) { + struct passwd *pwe; + + if (j->username) + pwe = getpwnam(j->username); + else + pwe = getpwuid(j->mach_uid); + + if (pwe) { + uid_t pwe_u = pwe->pw_uid; + uid_t pwe_g = pwe->pw_gid; + + if (pwe->pw_expire && time(NULL) >= pwe->pw_expire) { + job_log(j, LOG_ERR, "expired account: %s", j->username); + exit(EXIT_FAILURE); + } + if (j->init_groups) { + if (-1 == initgroups(j->username, gre ? gre_g : pwe_g)) { + job_log_error(j, LOG_ERR, "initgroups()"); + exit(EXIT_FAILURE); + } + } + if (!gre) { + if (-1 == setgid(pwe_g)) { + job_log_error(j, LOG_ERR, "setgid(%d)", pwe_g); + exit(EXIT_FAILURE); + } + } + if (-1 == setuid(pwe_u)) { + job_log_error(j, LOG_ERR, "setuid(%d)", pwe_u); + exit(EXIT_FAILURE); + } + } else { + if (j->username) { + job_log(j, LOG_WARNING, "getpwnam(\"%s\") failed", j->username); + } else { + job_log(j, LOG_WARNING, "getpwuid(\"%d\") failed", j->mach_uid); + } + exit(EXIT_FAILURE); + } + } + if (j->workingdir) + chdir(j->workingdir); + if (j->setmask) + umask(j->mask); + if (j->stdinpath) { + int sifd = open(j->stdinpath, O_RDONLY|O_NOCTTY); + if (sifd == -1) { + job_log_error(j, LOG_WARNING, "open(\"%s\", ...)", j->stdinpath); + } else { + launchd_assumes(dup2(sifd, STDIN_FILENO) != -1); + launchd_assumes(close(sifd) == 0); + } + } + if (j->stdoutpath) { + int sofd = open(j->stdoutpath, O_WRONLY|O_APPEND|O_CREAT|O_NOCTTY, DEFFILEMODE); + if (sofd == -1) { + job_log_error(j, LOG_WARNING, "open(\"%s\", ...)", j->stdoutpath); + } else { + launchd_assumes(dup2(sofd, STDOUT_FILENO) != -1); + launchd_assumes(close(sofd) == 0); + } + } + if (j->stderrpath) { + int sefd = open(j->stderrpath, O_WRONLY|O_APPEND|O_CREAT|O_NOCTTY, DEFFILEMODE); + if (sefd == -1) { + job_log_error(j, LOG_WARNING, "open(\"%s\", ...)", j->stderrpath); + } else { + launchd_assumes(dup2(sefd, STDERR_FILENO) != -1); + launchd_assumes(close(sefd) == 0); + } + } + + job_setup_env_from_other_jobs(root_job); + + SLIST_FOREACH(ei, &j->env, sle) + setenv(ei->key, ei->value, 1); + + setsid(); +} + +int +dir_has_files(const char *path) +{ + DIR *dd = opendir(path); + struct dirent *de; + bool r = 0; + + if (!dd) + return -1; + + while ((de = readdir(dd))) { + if (strcmp(de->d_name, ".") && strcmp(de->d_name, "..")) { + r = 1; + break; + } + } + + launchd_assumes(closedir(dd) == 0); + return r; +} + +void +calendarinterval_setalarm(struct jobcb *j, struct calendarinterval *ci) +{ + time_t later; + + later = cronemu(ci->when.tm_mon, ci->when.tm_mday, ci->when.tm_hour, ci->when.tm_min); + + if (ci->when.tm_wday != -1) { + time_t otherlater = cronemu_wday(ci->when.tm_wday, ci->when.tm_hour, ci->when.tm_min); + + if (ci->when.tm_mday == -1) { + later = otherlater; + } else { + later = later < otherlater ? later : otherlater; + } + } + + if (-1 == kevent_mod((uintptr_t)ci, EVFILT_TIMER, EV_ADD, NOTE_ABSOLUTE|NOTE_SECONDS, later, j)) { + job_log_error(j, LOG_ERR, "adding kevent alarm"); + } else { + job_log(j, LOG_INFO, "scheduled to run again at %s", ctime(&later)); + } +} + +size_t +job_prep_log_preface(struct jobcb *j, char *buf) +{ + size_t lsz = strlen(j->label); + char newlabel[lsz * 2 + 1]; + size_t i, o, r = 0; + + for (i = 0, o = 0; i < lsz; i++, o++) { + if (j->label[i] == '%') { + newlabel[o] = '%'; + o++; + newlabel[o] = '%'; + } else { + newlabel[o] = j->label[i]; + } + } + newlabel[o] = '\0'; + + if (j->parent) + r = job_prep_log_preface(j->parent, buf); + + return r + sprintf(buf + r, "%s%s", j->parent ? "/" : "", newlabel); +} + +void +job_log_error(struct jobcb *j, int pri, const char *msg, ...) +{ + char newmsg[10000]; + va_list ap; + size_t o; + + o = job_prep_log_preface(j, newmsg); + + sprintf(newmsg + o, ": %s: %s", msg, strerror(errno)); + + va_start(ap, msg); + vsyslog(pri, newmsg, ap); + va_end(ap); +} + +void +job_log(struct jobcb *j, int pri, const char *msg, ...) +{ + char newmsg[10000]; + va_list ap; + size_t o; + + o = job_prep_log_preface(j, newmsg); + + sprintf(newmsg + o, ": %s", msg); + + va_start(ap, msg); + vsyslog(pri, newmsg, ap); + va_end(ap); +} + +bool +watchpath_new(struct jobcb *j, const char *name, bool qdir) +{ + struct watchpath *wp = calloc(1, sizeof(struct watchpath) + strlen(name) + 1); + + if (!launchd_assumes(wp != NULL)) + return false; + + wp->is_qdir = qdir; + + wp->fd = -1; /* watchpath_watch() will open this */ + + strcpy(wp->name, name); + + SLIST_INSERT_HEAD(&j->vnodes, wp, sle); + + return true; +} + +void +watchpath_delete(struct jobcb *j, struct watchpath *wp) +{ + if (wp->fd != -1) + launchd_assumes(close(wp->fd) != -1); + + SLIST_REMOVE(&j->vnodes, wp, watchpath, sle); + + free(wp); +} + +void +watchpath_ignore(struct jobcb *j, struct watchpath *wp) +{ + if (wp->fd != -1) { + job_log(j, LOG_DEBUG, "Ignoring Vnode: %d", wp->fd); + launchd_assumes(kevent_mod(wp->fd, EVFILT_VNODE, EV_DELETE, 0, 0, NULL) != -1); + } +} + +void +watchpath_watch(struct jobcb *j, struct watchpath *wp) +{ + int fflags = NOTE_WRITE|NOTE_EXTEND|NOTE_ATTRIB|NOTE_LINK; + int qdir_file_cnt; + + if (!wp->is_qdir) + fflags |= NOTE_DELETE|NOTE_RENAME|NOTE_REVOKE; + + if (wp->fd == -1) + wp->fd = _fd(open(wp->name, O_EVTONLY|O_NOCTTY|O_NOFOLLOW)); + + if (wp->fd == -1) + return job_log_error(j, LOG_ERR, "Watchpath monitoring failed on \"%s\"", wp->name); + + job_log(j, LOG_DEBUG, "Watching Vnode: %d", wp->fd); + launchd_assumes(kevent_mod(wp->fd, EVFILT_VNODE, EV_ADD|EV_CLEAR, fflags, 0, j) != -1); + + if (!wp->is_qdir) + return; + + if (-1 == (qdir_file_cnt = dir_has_files(wp->name))) { + job_log_error(j, LOG_ERR, "dir_has_files(\"%s\", ...)", wp->name); + } else if (qdir_file_cnt > 0) { + job_start(j); + } +} + +void +watchpath_callback(struct jobcb *j, struct kevent *kev) +{ + struct watchpath *wp; + int dir_file_cnt; + + SLIST_FOREACH(wp, &j->vnodes, sle) { + if (wp->fd == (int)kev->ident) + break; + } + + launchd_assumes(wp != NULL); + + if ((NOTE_DELETE|NOTE_RENAME|NOTE_REVOKE) & kev->fflags) { + job_log(j, LOG_DEBUG, "Path invalidated: %s", wp->name); + launchd_assumes(close(wp->fd) == 0); + wp->fd = -1; /* this will get fixed in watchpath_watch() */ + } else if (!wp->is_qdir) { + job_log(j, LOG_DEBUG, "Watch path modified: %s", wp->name); + } else { + job_log(j, LOG_DEBUG, "Queue directory modified: %s", wp->name); + + if (-1 == (dir_file_cnt = dir_has_files(wp->name))) { + job_log_error(j, LOG_ERR, "dir_has_files(\"%s\", ...)", wp->name); + } else if (0 == dir_file_cnt) { + job_log(j, LOG_DEBUG, "Spurious wake up, directory is empty again: %s", wp->name); + return; + } + } + + job_start(j); +} + +bool +calendarinterval_new_from_obj(struct jobcb *j, launch_data_t obj) +{ + launch_data_t tmp_k; + struct tm tmptm; + + memset(&tmptm, 0, sizeof(0)); + + tmptm.tm_min = -1; + tmptm.tm_hour = -1; + tmptm.tm_mday = -1; + tmptm.tm_wday = -1; + tmptm.tm_mon = -1; + + if (LAUNCH_DATA_DICTIONARY != launch_data_get_type(obj)) + return false; + + if ((tmp_k = launch_data_dict_lookup(obj, LAUNCH_JOBKEY_CAL_MINUTE))) + tmptm.tm_min = launch_data_get_integer(tmp_k); + if ((tmp_k = launch_data_dict_lookup(obj, LAUNCH_JOBKEY_CAL_HOUR))) + tmptm.tm_hour = launch_data_get_integer(tmp_k); + if ((tmp_k = launch_data_dict_lookup(obj, LAUNCH_JOBKEY_CAL_DAY))) + tmptm.tm_mday = launch_data_get_integer(tmp_k); + if ((tmp_k = launch_data_dict_lookup(obj, LAUNCH_JOBKEY_CAL_WEEKDAY))) + tmptm.tm_wday = launch_data_get_integer(tmp_k); + if ((tmp_k = launch_data_dict_lookup(obj, LAUNCH_JOBKEY_CAL_MONTH))) + tmptm.tm_mon = launch_data_get_integer(tmp_k); + + return calendarinterval_new(j, &tmptm); +} + +bool +calendarinterval_new(struct jobcb *j, struct tm *w) +{ + struct calendarinterval *ci = calloc(1, sizeof(struct calendarinterval)); + + if (!launchd_assumes(ci != NULL)) + return false; + + ci->when = *w; + + SLIST_INSERT_HEAD(&j->cal_intervals, ci, sle); + + calendarinterval_setalarm(j, ci); + + return true; +} + +void +calendarinterval_delete(struct jobcb *j, struct calendarinterval *ci) +{ + launchd_assumes(kevent_mod((uintptr_t)ci, EVFILT_TIMER, EV_DELETE, 0, 0, NULL) != -1); + + SLIST_REMOVE(&j->cal_intervals, ci, calendarinterval, sle); + + free(ci); +} + +void +calendarinterval_callback(struct jobcb *j, struct kevent *kev) +{ + struct calendarinterval *ci; + + SLIST_FOREACH(ci, &j->cal_intervals, sle) { + if ((uintptr_t)ci == kev->ident) + break; + } + + if (launchd_assumes(ci != NULL)) { + calendarinterval_setalarm(j, ci); + job_start(j); + } +} + +bool +socketgroup_new(struct jobcb *j, const char *name, int *fds, unsigned int fd_cnt, bool junkfds) +{ + struct socketgroup *sg = calloc(1, sizeof(struct socketgroup) + strlen(name) + 1); + + if (!launchd_assumes(sg != NULL)) + return false; + + sg->fds = calloc(1, fd_cnt * sizeof(int)); + sg->fd_cnt = fd_cnt; + sg->junkfds = junkfds; + + if (!launchd_assumes(sg->fds != NULL)) { + free(sg); + return false; + } + + memcpy(sg->fds, fds, fd_cnt * sizeof(int)); + strcpy(sg->name, name); + + SLIST_INSERT_HEAD(&j->sockets, sg, sle); + + return true; +} + +void +socketgroup_delete(struct jobcb *j, struct socketgroup *sg) +{ + unsigned int i; + + for (i = 0; i < sg->fd_cnt; i++) + launchd_assumes(close(sg->fds[i]) != -1); + + SLIST_REMOVE(&j->sockets, sg, socketgroup, sle); + + free(sg->fds); + free(sg); +} + +void +socketgroup_ignore(struct jobcb *j, struct socketgroup *sg) +{ + char buf[10000]; + unsigned int i, buf_off = 0; + + if (sg->junkfds) + return; + + for (i = 0; i < sg->fd_cnt; i++) + buf_off += sprintf(buf + buf_off, " %d", sg->fds[i]); + + job_log(j, LOG_DEBUG, "Ignoring Sockets:%s", buf); + + for (i = 0; i < sg->fd_cnt; i++) + launchd_assumes(kevent_mod(sg->fds[i], EVFILT_READ, EV_DELETE, 0, 0, NULL) != -1); +} + +void +socketgroup_watch(struct jobcb *j, struct socketgroup *sg) +{ + char buf[10000]; + unsigned int i, buf_off = 0; + + if (sg->junkfds) + return; + + for (i = 0; i < sg->fd_cnt; i++) + buf_off += sprintf(buf + buf_off, " %d", sg->fds[i]); + + job_log(j, LOG_DEBUG, "Watching sockets:%s", buf); + + for (i = 0; i < sg->fd_cnt; i++) + launchd_assumes(kevent_mod(sg->fds[i], EVFILT_READ, EV_ADD, 0, 0, j) != -1); +} + +void +socketgroup_callback(struct jobcb *j, struct kevent *kev) +{ + job_start(j); +} + +bool +envitem_new(struct jobcb *j, const char *k, const char *v, bool global) +{ + struct envitem *ei = calloc(1, sizeof(struct envitem) + strlen(k) + 1 + strlen(v) + 1); + + if (!launchd_assumes(ei != NULL)) + return false; + + strcpy(ei->key, k); + ei->value = ei->key + strlen(k) + 1; + strcpy(ei->value, v); + + if (global) { + SLIST_INSERT_HEAD(&j->global_env, ei, sle); + } else { + SLIST_INSERT_HEAD(&j->env, ei, sle); + } + + return true; +} + +void +envitem_delete(struct jobcb *j, struct envitem *ei, bool global) +{ + if (global) { + SLIST_REMOVE(&j->global_env, ei, envitem, sle); + } else { + SLIST_REMOVE(&j->env, ei, envitem, sle); + } + + free(ei); +} + +void +envitem_setup(launch_data_t obj, const char *key, void *context) +{ + struct jobcb *j = context; + + if (launch_data_get_type(obj) != LAUNCH_DATA_STRING) + return; + + envitem_new(j, key, launch_data_get_string(obj), j->importing_global_env); +} + +bool +limititem_update(struct jobcb *j, int w, rlim_t r) +{ + struct limititem *li; + + SLIST_FOREACH(li, &j->limits, sle) { + if (li->which == w) + break; + } + + if (li == NULL) { + li = calloc(1, sizeof(struct limititem)); + + if (!launchd_assumes(li != NULL)) + return false; + + li->which = w; + } + + if (j->importing_hard_limits) { + li->lim.rlim_max = r; + li->sethard = true; + } else { + li->lim.rlim_cur = r; + li->setsoft = true; + } + + return true; +} + +void +limititem_delete(struct jobcb *j, struct limititem *li) +{ + SLIST_REMOVE(&j->limits, li, limititem, sle); + + free(li); +} + +void +limititem_setup(launch_data_t obj, const char *key, void *context) +{ + struct jobcb *j = context; + int i, limits_cnt = (sizeof(launchd_keys2limits) / sizeof(launchd_keys2limits[0])); + rlim_t rl; + + if (launch_data_get_type(obj) != LAUNCH_DATA_INTEGER) + return; + + rl = launch_data_get_integer(obj); + + for (i = 0; i < limits_cnt; i++) { + if (strcasecmp(launchd_keys2limits[i].key, key) == 0) + break; + } + + if (i == limits_cnt) + return; + + limititem_update(j, launchd_keys2limits[i].val, rl); +} + +bool +job_useless(struct jobcb *j) +{ + if (j->unload_at_exit) { + job_log(j, LOG_INFO, "Exited. Was only configured to run once."); + return true; + } else if (shutdown_in_progress) { + job_log(j, LOG_INFO, "Exited while shutdown in progress."); + return true; + } else if (j->failed_exits >= LAUNCHD_FAILED_EXITS_THRESHOLD) { + job_log(j, LOG_WARNING, "too many failures in succession"); + return true; + } else if (!j->checkedin && (!SLIST_EMPTY(&j->sockets) || !SLIST_EMPTY(&j->machservices))) { + job_log(j, LOG_WARNING, "Failed to check-in!"); + return true; + } else if (j->legacy_mach_job && SLIST_EMPTY(&j->machservices)) { + job_log(j, LOG_INFO, "Garbage collecting"); + return true; + } + + return false; +} + +bool +job_keepalive(struct jobcb *j) +{ + mach_msg_type_number_t statusCnt; + mach_port_status_t status; + struct semaphoreitem *si; + struct machservice *ms; + struct stat sb; + bool good_exit = (WIFEXITED(j->last_exit_status) && WEXITSTATUS(j->last_exit_status) == 0); + bool dispatch_others = false; + + if (j->runatload && j->start_time == 0) { + job_log(j, LOG_DEBUG, "KeepAlive check: job needs to run at least once."); + return true; + } + + if (!j->ondemand) { + job_log(j, LOG_DEBUG, "KeepAlive check: job configured to run continuously."); + return true; + } + + SLIST_FOREACH(ms, &j->machservices, sle) { + statusCnt = MACH_PORT_RECEIVE_STATUS_COUNT; + if (mach_port_get_attributes(mach_task_self(), ms->port, MACH_PORT_RECEIVE_STATUS, + (mach_port_info_t)&status, &statusCnt) != KERN_SUCCESS) + continue; + if (status.mps_msgcount) { + job_log(j, LOG_DEBUG, "KeepAlive check: job restarted due to %d queued Mach messages on service: %s", + status.mps_msgcount, ms->name); + return true; + } + } + + + SLIST_FOREACH(si, &j->semaphores, sle) { + bool wanted_state = false; + switch (si->why) { + case NETWORK_UP: + wanted_state = true; + case NETWORK_DOWN: + if (network_up == wanted_state) { + job_log(j, LOG_DEBUG, "KeepAlive check: job configured to run while the network is %s.", + wanted_state ? "up" : "down"); + return true; + } + break; + case SUCCESSFUL_EXIT: + wanted_state = true; + case FAILED_EXIT: + if (good_exit == wanted_state) { + job_log(j, LOG_DEBUG, "KeepAlive check: job configured to run while the exit state was %s.", + wanted_state ? "successful" : "failure"); + return true; + } + break; + case PATH_EXISTS: + wanted_state = true; + case PATH_MISSING: + if ((bool)(stat(si->what, &sb) == 0) == wanted_state) { + job_log(j, LOG_DEBUG, "KeepAlive check: job configured to run while the following path %s: %s", + wanted_state ? "exists" : "is missing", si->what); + return true; + } + dispatch_others = true; + break; + } + } + + /* Maybe another job has the inverse path based semaphore as this job */ + if (dispatch_others) + job_dispatch_all_other_semaphores(root_job, j); + + return false; +} + +const char * +job_prog(struct jobcb *j) +{ + if (j->prog) { + return j->prog; + } else if (j->argv) { + return j->argv[0]; + } else { + return ""; + } +} + +bool +job_active(struct jobcb *j) +{ + struct machservice *ms; + + if (j->req_port) + return true; + + if (j->p) + return true; + + if (j->priv_port_has_senders) { + if (j->start_time && !j->checkedin) { + if (j->legacy_mach_job) { + job_log(j, LOG_NOTICE, "Daemonized. Extremely expensive no-op."); + } else if (!j->unload_at_exit) { + job_log(j, LOG_ERR, "Daemonization is not supported under launchd."); + return false; + } + } + return true; + } + + SLIST_FOREACH(ms, &j->machservices, sle) { + if (ms->isActive) + return true; + } + + return false; +} + +pid_t +launchd_fork(void) +{ + return job_fork(root_job); +} + +pid_t +job_fork(struct jobcb *j) +{ + mach_port_t p = j->bs_port; + pid_t r = -1; + + sigprocmask(SIG_BLOCK, &blocked_signals, NULL); + + launchd_assumes(launchd_mport_make_send(p) == KERN_SUCCESS); + launchd_assumes(launchd_set_bport(p) == KERN_SUCCESS); + launchd_assumes(launchd_mport_deallocate(p) == KERN_SUCCESS); + + r = fork(); + + if (r != 0) { + launchd_assumes(launchd_set_bport(MACH_PORT_NULL) == KERN_SUCCESS); + } else if (r == 0) { + size_t i; + + for (i = 0; i < NSIG; i++) { + if (sigismember(&blocked_signals, i)) + signal(i, SIG_DFL); + } + } + + sigprocmask(SIG_UNBLOCK, &blocked_signals, NULL); + + return r; +} + +void +machservice_resetport(struct jobcb *j, struct machservice *ms) +{ + launchd_assumes(launchd_mport_close_recv(ms->port) == KERN_SUCCESS); + launchd_assumes(launchd_mport_deallocate(ms->port) == KERN_SUCCESS); + launchd_assumes(launchd_mport_create_recv(&ms->port) == KERN_SUCCESS); + launchd_assumes(launchd_mport_make_send(ms->port) == KERN_SUCCESS); +} + +struct machservice * +machservice_new(struct jobcb *j, const char *name, mach_port_t *serviceport) +{ + struct machservice *ms; + + if ((ms = calloc(1, sizeof(struct machservice) + strlen(name) + 1)) == NULL) + return NULL; + + strcpy(ms->name, name); + ms->job = j; + + if (*serviceport == MACH_PORT_NULL) { + if (!launchd_assumes(launchd_mport_create_recv(&ms->port) == KERN_SUCCESS)) + goto out_bad; + + if (!launchd_assumes(launchd_mport_make_send(ms->port) == KERN_SUCCESS)) + goto out_bad2; + *serviceport = ms->port; + ms->isActive = false; + ms->recv = true; + } else { + ms->port = *serviceport; + ms->isActive = true; + } + + SLIST_INSERT_HEAD(&j->machservices, ms, sle); + + job_log(j, LOG_INFO, "Mach service added: %s", name); + + return ms; +out_bad2: + launchd_assumes(launchd_mport_close_recv(ms->port) == KERN_SUCCESS); +out_bad: + free(ms); + return NULL; +} + +bootstrap_status_t +machservice_status(struct machservice *ms) +{ + if (ms->isActive) { + return BOOTSTRAP_STATUS_ACTIVE; + } else if (ms->job->ondemand) { + return BOOTSTRAP_STATUS_ON_DEMAND; + } else { + return BOOTSTRAP_STATUS_INACTIVE; + } +} + +void +machservice_setup_options(launch_data_t obj, const char *key, void *context) +{ + struct machservice *ms = context; + mach_port_t mhp = mach_host_self(); + mach_port_t mts = mach_task_self(); + thread_state_flavor_t f = 0; + int which_port; + bool b; + +#if defined (__ppc__) + f = PPC_THREAD_STATE64; +#elif defined(__i386__) + f = x86_THREAD_STATE; +#endif + + if (!launchd_assumes(mhp != MACH_PORT_NULL)) { + return; + } + + switch (launch_data_get_type(obj)) { + case LAUNCH_DATA_INTEGER: + which_port = launch_data_get_integer(obj); + if (strcasecmp(key, LAUNCH_JOBKEY_MACH_TASKSPECIALPORT) == 0) { + launchd_assumes((errno = task_set_special_port(mts, which_port, ms->port)) == KERN_SUCCESS); + } else if (strcasecmp(key, LAUNCH_JOBKEY_MACH_HOSTSPECIALPORT) == 0 && getpid() == 1) { + launchd_assumes((errno = host_set_special_port(mhp, which_port, ms->port)) == KERN_SUCCESS); + } + case LAUNCH_DATA_BOOL: + b = launch_data_get_bool(obj); + if (strcasecmp(key, LAUNCH_JOBKEY_MACH_RESETATCLOSE) == 0) { + ms->reset = b; + } else if (strcasecmp(key, LAUNCH_JOBKEY_MACH_HIDEUNTILCHECKIN) == 0) { + ms->hide = b; + } else if (strcasecmp(key, LAUNCH_JOBKEY_MACH_EXCEPTIONSERVER) == 0) { + launchd_assumes(task_set_exception_ports(mts, EXC_MASK_ALL, ms->port, + EXCEPTION_STATE_IDENTITY, f) == KERN_SUCCESS); + } else if (strcasecmp(key, LAUNCH_JOBKEY_MACH_KUNCSERVER) == 0) { + ms->kUNCServer = b; + launchd_assumes(host_set_UNDServer(mhp, ms->port) == KERN_SUCCESS); + } + break; + default: + break; + } + + launchd_assumes(launchd_mport_deallocate(mhp) == KERN_SUCCESS); +} + +void +machservice_setup(launch_data_t obj, const char *key, void *context) +{ + struct jobcb *j = context; + struct machservice *ms; + mach_port_t p = MACH_PORT_NULL; + + if ((ms = job_lookup_service(j->parent, key, false))) { + job_log(j, LOG_WARNING, "Conflict with job: %s over Mach service: %s", ms->job->label, key); + return; + } + + if ((ms = machservice_new(j, key, &p)) == NULL) { + job_log_error(j, LOG_WARNING, "Cannot add service: %s", key); + return; + } + + ms->isActive = false; + + if (launch_data_get_type(obj) == LAUNCH_DATA_DICTIONARY) { + launch_data_dict_iterate(obj, machservice_setup_options, ms); + } +} + +struct jobcb * +job_parent(struct jobcb *j) +{ + return j->parent; +} + +void +job_uncork_fork(struct jobcb *j) +{ + pid_t c = j->p; + + if (j->stall_before_exec) { + job_log(j, LOG_DEBUG, "Uncorking the fork()."); + /* this unblocks the child and avoids a race + * between the above fork() and the kevent_mod() */ + write(j->execfd, &c, sizeof(c)); + j->stall_before_exec = false; + } else { + job_log(j, LOG_WARNING, "Attempt to uncork a job that isn't in the middle of a fork()."); + } +} + +void +job_foreach_service(struct jobcb *j, void (*bs_iter)(struct machservice *, void *), void *context, bool include_subjobs) +{ + struct machservice *ms; + struct jobcb *ji; + + j = job_get_bs(j); + + if (include_subjobs) { + SLIST_FOREACH(ji, &j->jobs, sle) { + if (ji->req_port) + continue; + + SLIST_FOREACH(ms, &ji->machservices, sle) + bs_iter(ms, context); + } + } + + SLIST_FOREACH(ms, &j->machservices, sle) + bs_iter(ms, context); +} + +struct jobcb * +job_new_bootstrap(struct jobcb *p, mach_port_t requestorport, mach_port_t checkin_port) +{ + char bslabel[1024] = "100000"; + struct jobcb *j; + + if (requestorport == MACH_PORT_NULL) { + if (p) { + job_log(p, LOG_ERR, "Mach sub-bootstrap create request requires a requester port"); + } + return NULL; + } + + j = job_new(p, bslabel, NULL, NULL, NULL, requestorport); + + if (j == NULL) + return NULL; + + if (checkin_port != MACH_PORT_NULL) { + j->bs_port = checkin_port; + } else if (!launchd_assumes(launchd_mport_create_recv(&j->bs_port) == KERN_SUCCESS)) { + goto out_bad; + } + + sprintf(j->label, "%d", MACH_PORT_INDEX(j->bs_port)); + + if (!launchd_assumes(launchd_mport_request_callback(j->bs_port, j, true) == KERN_SUCCESS)) + goto out_bad; + + if (p) { + job_log(p, LOG_DEBUG, "Mach sub-bootstrap created: %s", j->label); + } + + return j; + +out_bad: + if (j) + job_remove(j); + return NULL; +} + +void +job_delete_anything_with_port(struct jobcb *j, mach_port_t port) +{ + struct machservice *ms, *next_ms; + struct jobcb *ji, *jn; + + /* Mach ports, unlike Unix descriptors, are reference counted. In other + * words, when some program hands us a second or subsequent send right + * to a port we already have open, the Mach kernel gives us the same + * port number back and increments an reference count associated with + * the port. This forces us, when discovering that a receive right at + * the other end has been deleted, to wander all of our objects to see + * what weird places clients might have handed us the same send right + * to use. + */ + + if (j->req_port == port) + return job_remove(j); + + SLIST_FOREACH_SAFE(ji, &j->jobs, sle, jn) + job_delete_anything_with_port(ji, port); + + SLIST_FOREACH_SAFE(ms, &j->machservices, sle, next_ms) { + if (ms->port == port) + machservice_delete(ms); + } +} + +struct machservice * +job_lookup_service(struct jobcb *j, const char *name, bool check_parent) +{ + struct machservice *ms; + struct jobcb *ji; + + j = job_get_bs(j); + + SLIST_FOREACH(ji, &j->jobs, sle) { + if (ji->req_port) + continue; + + SLIST_FOREACH(ms, &ji->machservices, sle) { + if (strcmp(name, ms->name) == 0) + return ms; + } + } + + SLIST_FOREACH(ms, &j->machservices, sle) { + if (strcmp(name, ms->name) == 0) + return ms; + } + + if (j->parent == NULL) + return NULL; + + if (!check_parent) + return NULL; + + return job_lookup_service(j->parent, name, true); +} + +mach_port_t +machservice_port(struct machservice *ms) +{ + return ms->port; +} + +struct jobcb * +machservice_job(struct machservice *ms) +{ + return ms->job; +} + +bool +machservice_hidden(struct machservice *ms) +{ + return ms->hide; +} + +bool +machservice_active(struct machservice *ms) +{ + return ms->isActive; +} + +const char * +machservice_name(struct machservice *ms) +{ + return ms->name; +} + +void +machservice_delete(struct machservice *ms) +{ + if (ms->recv) { + if (ms->isActive) { + /* FIXME we should cancel the notification */ + } else { + launchd_assumes(launchd_mport_close_recv(ms->port) == KERN_SUCCESS); + } + } + + launchd_assumes(launchd_mport_deallocate(ms->port) == KERN_SUCCESS); + + job_log(ms->job, LOG_INFO, "Mach service deleted: %s", ms->name); + + SLIST_REMOVE(&ms->job->machservices, ms, machservice, sle); + + free(ms); +} + +void +machservice_watch(struct machservice *ms) +{ + mach_msg_id_t which = MACH_NOTIFY_DEAD_NAME; + + ms->isActive = true; + + if (ms->job->req_port == MACH_PORT_NULL) { + which = MACH_NOTIFY_PORT_DESTROYED; + job_checkin(ms->job); + } + + launchd_assumes(launchd_mport_notify_req(ms->port, which) == KERN_SUCCESS); +} + +#define NELEM(x) (sizeof(x)/sizeof(x[0])) +#define END_OF(x) (&(x)[NELEM(x)]) + +char ** +mach_cmd2argv(const char *string) +{ + char *argv[100], args[1000]; + const char *cp; + char *argp = args, term, **argv_ret, *co; + unsigned int nargs = 0, i; + + for (cp = string; *cp;) { + while (isspace(*cp)) + cp++; + term = (*cp == '"') ? *cp++ : '\0'; + if (nargs < NELEM(argv)) + argv[nargs++] = argp; + while (*cp && (term ? *cp != term : !isspace(*cp)) && argp < END_OF(args)) { + if (*cp == '\\') + cp++; + *argp++ = *cp; + if (*cp) + cp++; + } + *argp++ = '\0'; + } + argv[nargs] = NULL; + + if (nargs == 0) + return NULL; + + argv_ret = malloc((nargs + 1) * sizeof(char *) + strlen(string) + 1); + + if (!launchd_assumes(argv_ret != NULL)) + return NULL; + + co = (char *)argv_ret + (nargs + 1) * sizeof(char *); + + for (i = 0; i < nargs; i++) { + strcpy(co, argv[i]); + argv_ret[i] = co; + co += strlen(argv[i]) + 1; + } + argv_ret[i] = NULL; + + return argv_ret; +} + +void +job_checkin(struct jobcb *j) +{ + j->checkedin = true; +} + +bool +job_ack_port_destruction(struct jobcb *j, mach_port_t p) +{ + struct jobcb *ji; + struct machservice *ms; + + SLIST_FOREACH(ji, &j->jobs, sle) { + if (job_ack_port_destruction(ji, p)) + return true; + } + + SLIST_FOREACH(ms, &j->machservices, sle) { + if (ms->port == p) + break; + } + + if (ms == NULL) + return false; + + ms->isActive = false; + + if (ms->reset) + machservice_resetport(j, ms); + + job_log(j, LOG_DEBUG, "Receive right returned to us: %s", ms->name); + + job_dispatch(j); + + return true; +} + +void +job_ack_no_senders(struct jobcb *j) +{ + j->priv_port_has_senders = false; + + job_log(j, LOG_DEBUG, "No more senders on privileged Mach bootstrap port"); + + job_dispatch(j); +} + +mach_port_t +job_get_reqport(struct jobcb *j) +{ + j->transfer_bstrap = true; + gc_this_job = j; + + return j->req_port; +} + +mach_port_t +job_get_bsport(struct jobcb *j) +{ + return j->bs_port; +} + +struct jobcb * +job_get_bs(struct jobcb *j) +{ + if (j->req_port) + return j; + + if (launchd_assumes(j->parent != NULL)) + return j->parent; + + return NULL; +} + +pid_t +job_get_pid(struct jobcb *j) +{ + return j->p; +} + +bool +semaphoreitem_new(struct jobcb *j, semaphore_reason_t why, const char *what) +{ + struct semaphoreitem *si; + size_t alloc_sz = sizeof(struct semaphoreitem); + + if (what) + alloc_sz += strlen(what) + 1; + + if (!launchd_assumes(si = calloc(1, alloc_sz))) + return false; + + si->why = why; + + if (what) + strcpy(si->what, what); + + SLIST_INSERT_HEAD(&j->semaphores, si, sle); + + return true; +} + +void +semaphoreitem_delete(struct jobcb *j, struct semaphoreitem *ri) +{ + SLIST_REMOVE(&j->semaphores, ri, semaphoreitem, sle); + + free(ri); +} + +void +semaphoreitem_setup_paths(launch_data_t obj, const char *key, void *context) +{ + struct jobcb *j = context; + semaphore_reason_t why; + + why = launch_data_get_bool(obj) ? PATH_EXISTS : PATH_MISSING; + + semaphoreitem_new(j, why, key); +} + +void +semaphoreitem_setup(launch_data_t obj, const char *key, void *context) +{ + struct jobcb *j = context; + semaphore_reason_t why; + + if (strcasecmp(key, LAUNCH_JOBKEY_KEEPALIVE_NETWORKSTATE) == 0) { + why = launch_data_get_bool(obj) ? NETWORK_UP : NETWORK_DOWN; + semaphoreitem_new(j, why, NULL); + } else if (strcasecmp(key, LAUNCH_JOBKEY_KEEPALIVE_SUCCESSFULEXIT) == 0) { + why = launch_data_get_bool(obj) ? SUCCESSFUL_EXIT : FAILED_EXIT; + semaphoreitem_new(j, why, NULL); + j->runatload = true; + } else if (strcasecmp(key, LAUNCH_JOBKEY_KEEPALIVE_PATHSTATE) == 0 && + launch_data_get_type(obj) == LAUNCH_DATA_DICTIONARY) { + launch_data_dict_iterate(obj, semaphoreitem_setup_paths, j); + } +} + +void +job_dispatch_all_other_semaphores(struct jobcb *j, struct jobcb *nj) +{ + struct jobcb *ji; + + if (j == nj) + return; + + if (!SLIST_EMPTY(&j->semaphores)) + job_dispatch(j); + + SLIST_FOREACH(ji, &j->jobs, sle) + job_dispatch_all_other_semaphores(ji, nj); +} + +time_t +cronemu(int mon, int mday, int hour, int min) +{ + struct tm workingtm; + time_t now; + + now = time(NULL); + workingtm = *localtime(&now); + + workingtm.tm_isdst = -1; + workingtm.tm_sec = 0; + workingtm.tm_min++; + + while (!cronemu_mon(&workingtm, mon, mday, hour, min)) { + workingtm.tm_year++; + workingtm.tm_mon = 0; + workingtm.tm_mday = 1; + workingtm.tm_hour = 0; + workingtm.tm_min = 0; + mktime(&workingtm); + } + + return mktime(&workingtm); +} + +time_t +cronemu_wday(int wday, int hour, int min) +{ + struct tm workingtm; + time_t now; + + now = time(NULL); + workingtm = *localtime(&now); + + workingtm.tm_isdst = -1; + workingtm.tm_sec = 0; + workingtm.tm_min++; + + if (wday == 7) + wday = 0; + + while (!(workingtm.tm_wday == wday && cronemu_hour(&workingtm, hour, min))) { + workingtm.tm_mday++; + workingtm.tm_hour = 0; + workingtm.tm_min = 0; + mktime(&workingtm); + } + + return mktime(&workingtm); +} + +bool +cronemu_mon(struct tm *wtm, int mon, int mday, int hour, int min) +{ + if (mon == -1) { + struct tm workingtm = *wtm; + int carrytest; + + while (!cronemu_mday(&workingtm, mday, hour, min)) { + workingtm.tm_mon++; + workingtm.tm_mday = 1; + workingtm.tm_hour = 0; + workingtm.tm_min = 0; + carrytest = workingtm.tm_mon; + mktime(&workingtm); + if (carrytest != workingtm.tm_mon) + return false; + } + *wtm = workingtm; + return true; + } + + if (mon < wtm->tm_mon) + return false; + + if (mon > wtm->tm_mon) { + wtm->tm_mon = mon; + wtm->tm_mday = 1; + wtm->tm_hour = 0; + wtm->tm_min = 0; + } + + return cronemu_mday(wtm, mday, hour, min); +} + +bool +cronemu_mday(struct tm *wtm, int mday, int hour, int min) +{ + if (mday == -1) { + struct tm workingtm = *wtm; + int carrytest; + + while (!cronemu_hour(&workingtm, hour, min)) { + workingtm.tm_mday++; + workingtm.tm_hour = 0; + workingtm.tm_min = 0; + carrytest = workingtm.tm_mday; + mktime(&workingtm); + if (carrytest != workingtm.tm_mday) + return false; + } + *wtm = workingtm; + return true; + } + + if (mday < wtm->tm_mday) + return false; + + if (mday > wtm->tm_mday) { + wtm->tm_mday = mday; + wtm->tm_hour = 0; + wtm->tm_min = 0; + } + + return cronemu_hour(wtm, hour, min); +} + +bool +cronemu_hour(struct tm *wtm, int hour, int min) +{ + if (hour == -1) { + struct tm workingtm = *wtm; + int carrytest; + + while (!cronemu_min(&workingtm, min)) { + workingtm.tm_hour++; + workingtm.tm_min = 0; + carrytest = workingtm.tm_hour; + mktime(&workingtm); + if (carrytest != workingtm.tm_hour) + return false; + } + *wtm = workingtm; + return true; + } + + if (hour < wtm->tm_hour) + return false; + + if (hour > wtm->tm_hour) { + wtm->tm_hour = hour; + wtm->tm_min = 0; + } + + return cronemu_min(wtm, min); +} + +bool +cronemu_min(struct tm *wtm, int min) +{ + if (min == -1) + return true; + + if (min < wtm->tm_min) + return false; + + if (min > wtm->tm_min) { + wtm->tm_min = min; + } + + return true; +} diff --git a/launchd/src/launchd_core_logic.h b/launchd/src/launchd_core_logic.h new file mode 100644 index 0000000..1ef2011 --- /dev/null +++ b/launchd/src/launchd_core_logic.h @@ -0,0 +1,85 @@ +#ifndef __LAUNCHD_CORE_LOGIC__ +#define __LAUNCHD_CORE_LOGIC__ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_APACHE_LICENSE_HEADER_START@ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @APPLE_APACHE_LICENSE_HEADER_END@ + */ + +#define LAUNCHD_MIN_JOB_RUN_TIME 10 +#define LAUNCHD_REWARD_JOB_RUN_TIME 60 +#define LAUNCHD_FAILED_EXITS_THRESHOLD 10 + +#include "bootstrap_public.h" + +struct jobcb; +struct machservice; + + +struct machservice *machservice_new(struct jobcb *j, const char *name, mach_port_t *serviceport); +void machservice_delete(struct machservice *); +void machservice_watch(struct machservice *); +mach_port_t machservice_port(struct machservice *); +struct jobcb *machservice_job(struct machservice *); +bool machservice_hidden(struct machservice *); +bool machservice_active(struct machservice *); +const char *machservice_name(struct machservice *); +bootstrap_status_t machservice_status(struct machservice *); + + +struct jobcb *job_find(struct jobcb *j, const char *label); +struct jobcb *job_find_by_pid(struct jobcb *j, pid_t p); +struct jobcb *job_find_by_port(mach_port_t mp); +struct jobcb *job_import(launch_data_t pload); +launch_data_t job_import_bulk(launch_data_t pload); +struct jobcb *job_new(struct jobcb *p, const char *label, const char *prog, const char *const *argv, const char *stdinpath, mach_port_t); +struct jobcb *job_new_spawn(const char *label, const char *path, const char *workingdir, const char *const *argv, const char *const *env, mode_t *u_mask, bool w4d, bool fppc); +struct jobcb *job_new_via_mach_init(struct jobcb *jbs, const char *cmd, uid_t uid, bool ond); +struct jobcb *job_new_bootstrap(struct jobcb *p, mach_port_t requestorport, mach_port_t checkin_port); +launch_data_t job_export(struct jobcb *j); +launch_data_t job_export_all(void); +void job_dispatch(struct jobcb *j); +void job_dispatch_all_other_semaphores(struct jobcb *j, struct jobcb *nj); +void job_start(struct jobcb *j); +void job_stop(struct jobcb *j); +bool job_active(struct jobcb *j); +void job_checkin(struct jobcb *j); +const char *job_prog(struct jobcb *j); +void job_remove(struct jobcb *j); +void job_remove_all_inactive(struct jobcb *j); +bool job_ack_port_destruction(struct jobcb *j, mach_port_t p); +void job_ack_no_senders(struct jobcb *j); +pid_t job_get_pid(struct jobcb *j); +mach_port_t job_get_bsport(struct jobcb *j); +mach_port_t job_get_reqport(struct jobcb *j); +struct jobcb *job_get_bs(struct jobcb *j); +void job_delete_anything_with_port(struct jobcb *jbs, mach_port_t port); +struct jobcb *job_parent(struct jobcb *j); +void job_uncork_fork(struct jobcb *j); +struct machservice *job_lookup_service(struct jobcb *jbs, const char *name, bool check_parent); +void job_foreach_service(struct jobcb *jbs, void (*bs_iter)(struct machservice *, void *), void *context, bool include_subjobs); +void job_log(struct jobcb *j, int pri, const char *msg, ...) __attribute__((format(printf, 3, 4))); +void job_log_error(struct jobcb *j, int pri, const char *msg, ...) __attribute__((format(printf, 3, 4))); +kern_return_t job_handle_mpm_wait(struct jobcb *j, mach_port_t srp, int *waitstatus); + +extern size_t total_children; + +extern struct jobcb *root_job; + +extern struct jobcb *gc_this_job; + +#endif diff --git a/launchd/src/launchd_debugd.8 b/launchd/src/launchd_debugd.8 deleted file mode 100644 index f8464e5..0000000 --- a/launchd/src/launchd_debugd.8 +++ /dev/null @@ -1,21 +0,0 @@ -.Dd February 2, 2005 -.Dt launchd_debugd 8 -.Os Darwin -.Sh NAME -.Nm launchd_debugd -.Nd Simple HTTP server to display job data -.Sh SYNOPSIS -.Nm -.Sh DESCRIPTION -.Nm -Provides a simple HTTP server to display all jobs loaded into -.Nm launchd . -This program may be merged into -.Nm launchd -in the future. -.Pp -To enable (as root): "launchctl load -w /System/Library/LaunchDaemons/com.apple.launchd_helperd.plist" -.Sh SEE ALSO -.Xr launchctl 1 , -.Xr launchd.plist 5 , -.Xr launchd 8 diff --git a/launchd/src/launchd_internal.defs b/launchd/src/launchd_internal.defs new file mode 100644 index 0000000..33dc1c0 --- /dev/null +++ b/launchd/src/launchd_internal.defs @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_APACHE_LICENSE_HEADER_START@ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @APPLE_APACHE_LICENSE_HEADER_END@ + */ + +subsystem launchd_internal 137000; + +#include +#include + +serverprefix x_; + +routine handle_kqueue( + __port : mach_port_t; + __fd : integer_t); + +routine handle_mport( + __port : mach_port_t); diff --git a/launchd/src/launchd_mach_ipc.c b/launchd/src/launchd_mach_ipc.c new file mode 100644 index 0000000..91c571b --- /dev/null +++ b/launchd/src/launchd_mach_ipc.c @@ -0,0 +1,916 @@ +/* + * Copyright (c) 1999-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_APACHE_LICENSE_HEADER_START@ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @APPLE_APACHE_LICENSE_HEADER_END@ + */ +/* + * bootstrap -- fundamental service initiator and port server + * Mike DeMoney, NeXT, Inc. + * Copyright, 1990. All rights reserved. + * + * bootstrap.c -- implementation of bootstrap main service loop + */ + +static const char *const __rcs_file_version__ = "$Revision: 1.52 $"; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bootstrap_public.h" +#include "bootstrap_private.h" +#include "bootstrap.h" +#include "bootstrapServer.h" +#include "notifyServer.h" +#include "launchd_internal.h" +#include "launchd_internalServer.h" +#include "launchd.h" +#include "launchd_core_logic.h" +#include "launch_priv.h" +#include "launchd_unix_ipc.h" + +struct ldcred { + uid_t euid; + uid_t uid; + gid_t egid; + gid_t gid; + pid_t pid; + au_asid_t asid; +}; + +static au_asid_t inherited_asid = 0; + +static bool canReceive(mach_port_t); +static void init_ports(void); +static void *mport_demand_loop(void *arg); +static void audit_token_to_launchd_cred(audit_token_t au_tok, struct ldcred *ldc); + +static mach_port_t inherited_bootstrap_port = MACH_PORT_NULL; +static mach_port_t demand_port_set = MACH_PORT_NULL; +static size_t port_to_obj_size = 0; +static void **port_to_obj = NULL; +static pthread_t demand_thread; + +static bool trusted_client_check(struct jobcb *j, struct ldcred *ldc); + +struct jobcb * +job_find_by_port(mach_port_t mp) +{ + return port_to_obj[MACH_PORT_INDEX(mp)]; +} + +kern_return_t +x_handle_mport(mach_port_t junk __attribute__((unused))) +{ + mach_port_name_array_t members; + mach_msg_type_number_t membersCnt; + mach_port_status_t status; + mach_msg_type_number_t statusCnt; + struct kevent kev; + unsigned int i; + + if (!launchd_assumes(mach_port_get_set_status(mach_task_self(), demand_port_set, &members, &membersCnt) == KERN_SUCCESS)) + return 1; + + for (i = 0; i < membersCnt; i++) { + statusCnt = MACH_PORT_RECEIVE_STATUS_COUNT; + if (mach_port_get_attributes(mach_task_self(), members[i], MACH_PORT_RECEIVE_STATUS, + (mach_port_info_t)&status, &statusCnt) != KERN_SUCCESS) + continue; + + if (status.mps_msgcount) { + EV_SET(&kev, members[i], EVFILT_MACHPORT, 0, 0, 0, job_find_by_port(members[i])); + (*((kq_callback *)kev.udata))(kev.udata, &kev); + /* the callback may have tainted our ability to continue this for loop */ + break; + } + } + + launchd_assumes(vm_deallocate(mach_task_self(), (vm_address_t)members, + (vm_size_t) membersCnt * sizeof(mach_port_name_t)) == KERN_SUCCESS); + + return 0; +} + +void +mach_init_init(mach_port_t req_port, mach_port_t checkin_port, + name_array_t l2l_names, mach_port_array_t l2l_ports, mach_msg_type_number_t l2l_cnt) +{ + mach_msg_type_number_t l2l_i; + auditinfo_t inherited_audit; + pthread_attr_t attr; + + getaudit(&inherited_audit); + inherited_asid = inherited_audit.ai_asid; + + init_ports(); + + launchd_assert((root_job = job_new_bootstrap(NULL, req_port ? req_port : mach_task_self(), checkin_port)) != NULL); + + launchd_assumes(launchd_get_bport(&inherited_bootstrap_port) == KERN_SUCCESS); + + if (getpid() != 1) + launchd_assumes(inherited_bootstrap_port != MACH_PORT_NULL); + + /* We set this explicitly as we start each child */ + launchd_assumes(launchd_set_bport(MACH_PORT_NULL) == KERN_SUCCESS); + + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN); + + launchd_assert(pthread_create(&demand_thread, &attr, mport_demand_loop, NULL) == 0); + + pthread_attr_destroy(&attr); + + /* cut off the Libc cache, we don't want to deadlock against ourself */ + bootstrap_port = MACH_PORT_NULL; + + if (l2l_names == NULL) + return; + + for (l2l_i = 0; l2l_i < l2l_cnt; l2l_i++) { + struct machservice *ms; + + if ((ms = machservice_new(root_job, l2l_names[l2l_i], l2l_ports + l2l_i))) + machservice_watch(ms); + } +} + +void mach_init_reap(void) +{ + void *status; + + launchd_assumes(mach_port_destroy(mach_task_self(), demand_port_set) == KERN_SUCCESS); + + launchd_assumes(pthread_join(demand_thread, &status) == 0); +} + +void +init_ports(void) +{ + launchd_assert((errno = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_PORT_SET, + &demand_port_set)) == KERN_SUCCESS); +} + +void * +mport_demand_loop(void *arg __attribute__((unused))) +{ + mach_msg_empty_rcv_t dummy; + kern_return_t kr; + + for (;;) { + kr = mach_msg(&dummy.header, MACH_RCV_MSG|MACH_RCV_LARGE, 0, 0, demand_port_set, 0, MACH_PORT_NULL); + if (kr == MACH_RCV_PORT_CHANGED) { + break; + } else if (!launchd_assumes(kr == MACH_RCV_TOO_LARGE)) { + continue; + } + launchd_assumes(handle_mport(launchd_internal_port) == 0); + } + + return NULL; +} + +boolean_t +launchd_mach_ipc_demux(mach_msg_header_t *Request, mach_msg_header_t *Reply) +{ + if (bootstrap_server_routine(Request)) + return bootstrap_server(Request, Reply); + + return notify_server(Request, Reply); +} + +bool +canReceive(mach_port_t port) +{ + mach_port_type_t p_type; + + if (!launchd_assumes(mach_port_type(mach_task_self(), port, &p_type) == KERN_SUCCESS)) + return false; + + return ((p_type & MACH_PORT_TYPE_RECEIVE) != 0); +} + +kern_return_t +launchd_set_bport(mach_port_t name) +{ + return errno = task_set_bootstrap_port(mach_task_self(), name); +} + +kern_return_t +launchd_get_bport(mach_port_t *name) +{ + return errno = task_get_bootstrap_port(mach_task_self(), name); +} + +kern_return_t +launchd_mport_notify_req(mach_port_t name, mach_msg_id_t which) +{ + mach_port_mscount_t msgc = (which == MACH_NOTIFY_NO_SENDERS) ? 1 : 0; + mach_port_t previous, where = (which == MACH_NOTIFY_NO_SENDERS) ? name : launchd_internal_port; + + if (which == MACH_NOTIFY_NO_SENDERS) { + /* Always make sure the send count is zero, in case a receive right is reused */ + errno = mach_port_set_mscount(mach_task_self(), name, 0); + if (errno != KERN_SUCCESS) + return errno; + } + + errno = mach_port_request_notification(mach_task_self(), name, which, msgc, where, + MACH_MSG_TYPE_MAKE_SEND_ONCE, &previous); + + if (errno == 0 && previous != MACH_PORT_NULL) + launchd_assumes(launchd_mport_deallocate(previous) == KERN_SUCCESS); + + return errno; +} + +kern_return_t +launchd_mport_request_callback(mach_port_t name, void *obj, bool readmsg) +{ + size_t needed_size; + + if (!obj) + return errno = mach_port_move_member(mach_task_self(), name, MACH_PORT_NULL); + + needed_size = (MACH_PORT_INDEX(name) + 1) * sizeof(void *); + + if (needed_size > port_to_obj_size) { + if (port_to_obj == NULL) { + launchd_assumes((port_to_obj = calloc(1, needed_size * 2)) != NULL); + } else { + launchd_assumes((port_to_obj = reallocf(port_to_obj, needed_size * 2)) != NULL); + memset((uint8_t *)port_to_obj + port_to_obj_size, 0, needed_size * 2 - port_to_obj_size); + } + port_to_obj_size = needed_size * 2; + } + + port_to_obj[MACH_PORT_INDEX(name)] = obj; + + return errno = mach_port_move_member(mach_task_self(), name, readmsg ? ipc_port_set : demand_port_set); +} + +kern_return_t +launchd_mport_make_send(mach_port_t name) +{ + return errno = mach_port_insert_right(mach_task_self(), name, name, MACH_MSG_TYPE_MAKE_SEND); +} + +kern_return_t +launchd_mport_close_recv(mach_port_t name) +{ + if (launchd_assumes(port_to_obj != NULL)) { + port_to_obj[MACH_PORT_INDEX(name)] = NULL; + return errno = mach_port_mod_refs(mach_task_self(), name, MACH_PORT_RIGHT_RECEIVE, -1); + } else { + return errno = KERN_FAILURE; + } +} + +kern_return_t +launchd_mport_create_recv(mach_port_t *name) +{ + return errno = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, name); +} + +kern_return_t +launchd_mport_deallocate(mach_port_t name) +{ + return errno = mach_port_deallocate(mach_task_self(), name); +} + +void +audit_token_to_launchd_cred(audit_token_t au_tok, struct ldcred *ldc) +{ + audit_token_to_au32(au_tok, /* audit UID */ NULL, + &ldc->euid, &ldc->egid, + &ldc->uid, &ldc->gid, &ldc->pid, + &ldc->asid, /* au_tid_t */ NULL); +} + +kern_return_t +x_bootstrap_create_server(mach_port_t bp, cmd_t server_cmd, uid_t server_uid, boolean_t on_demand, + audit_token_t au_tok, mach_port_t *server_portp) +{ + struct jobcb *js, *j = job_find_by_port(bp); + struct ldcred ldc; + + audit_token_to_launchd_cred(au_tok, &ldc); + + job_log(j, LOG_DEBUG, "Server create attempt: %s", server_cmd); + +#define LET_MERE_MORTALS_ADD_SERVERS_TO_PID1 + /* XXX - This code should go away once the per session launchd is integrated with the rest of the system */ + #ifdef LET_MERE_MORTALS_ADD_SERVERS_TO_PID1 + if (getpid() == 1) { + if (ldc.euid != 0 && ldc.euid != server_uid) { + job_log(j, LOG_WARNING, "Server create: \"%s\": Will run as UID %d, not UID %d as they told us to", + server_cmd, ldc.euid, server_uid); + server_uid = ldc.euid; + } + } else +#endif + if (!trusted_client_check(j, &ldc)) { + return BOOTSTRAP_NOT_PRIVILEGED; + } else if (server_uid != getuid()) { + job_log(j, LOG_WARNING, "Server create: \"%s\": As UID %d, we will not be able to switch to UID %d", + server_cmd, getuid(), server_uid); + server_uid = getuid(); + } + + js = job_new_via_mach_init(j, server_cmd, server_uid, on_demand); + + if (js == NULL) + return BOOTSTRAP_NO_MEMORY; + + *server_portp = job_get_bsport(js); + return BOOTSTRAP_SUCCESS; +} + +kern_return_t +x_bootstrap_getsocket(mach_port_t bp, name_t spr) +{ + if (!sockpath) { + return BOOTSTRAP_NO_MEMORY; + } else if (getpid() == 1) { + return BOOTSTRAP_NOT_PRIVILEGED; + } + + strncpy(spr, sockpath, sizeof(name_t)); + + return BOOTSTRAP_SUCCESS; +} + +kern_return_t +x_bootstrap_unprivileged(mach_port_t bp, mach_port_t *unprivportp) +{ + struct jobcb *j = job_find_by_port(bp); + + job_log(j, LOG_DEBUG, "Requested unprivileged bootstrap port"); + + j = job_get_bs(j); + + *unprivportp = job_get_bsport(j); + + return BOOTSTRAP_SUCCESS; +} + + +kern_return_t +x_bootstrap_check_in(mach_port_t bp, name_t servicename, audit_token_t au_tok, mach_port_t *serviceportp) +{ + static pid_t last_warned_pid = 0; + struct jobcb *j = job_find_by_port(bp); + struct machservice *ms; + struct ldcred ldc; + + audit_token_to_launchd_cred(au_tok, &ldc); + + trusted_client_check(j, &ldc); + + ms = job_lookup_service(j, servicename, true); + + if (ms == NULL) { + job_log(j, LOG_DEBUG, "Check-in of Mach service failed. Unknown: %s", servicename); + return BOOTSTRAP_UNKNOWN_SERVICE; + } + if (machservice_job(ms) != j) { + if (last_warned_pid != ldc.pid) { + job_log(j, LOG_NOTICE, "Check-in of Mach service failed. PID %d is not privileged: %s", + ldc.pid, servicename); + last_warned_pid = ldc.pid; + } + return BOOTSTRAP_NOT_PRIVILEGED; + } + if (!canReceive(machservice_port(ms))) { + launchd_assumes(machservice_active(ms)); + job_log(j, LOG_DEBUG, "Check-in of Mach service failed. Already active: %s", servicename); + return BOOTSTRAP_SERVICE_ACTIVE; + } + + machservice_watch(ms); + + job_log(j, LOG_INFO, "Check-in of service: %s", servicename); + + *serviceportp = machservice_port(ms); + return BOOTSTRAP_SUCCESS; +} + +kern_return_t +x_bootstrap_register(mach_port_t bp, audit_token_t au_tok, name_t servicename, mach_port_t serviceport) +{ + struct jobcb *j = job_find_by_port(bp); + struct machservice *ms; + struct ldcred ldc; + + audit_token_to_launchd_cred(au_tok, &ldc); + + trusted_client_check(j, &ldc); + + job_log(j, LOG_DEBUG, "Mach service registration attempt: %s", servicename); + + ms = job_lookup_service(j, servicename, false); + + if (ms) { + if (machservice_job(ms) != j) + return BOOTSTRAP_NOT_PRIVILEGED; + if (machservice_active(ms)) { + job_log(j, LOG_DEBUG, "Mach service registration failed. Already active: %s", servicename); + launchd_assumes(!canReceive(machservice_port(ms))); + return BOOTSTRAP_SERVICE_ACTIVE; + } + job_checkin(machservice_job(ms)); + machservice_delete(ms); + } + + if (serviceport != MACH_PORT_NULL) { + if ((ms = machservice_new(job_get_bs(j), servicename, &serviceport))) { + machservice_watch(ms); + } else { + return BOOTSTRAP_NO_MEMORY; + } + } + + return BOOTSTRAP_SUCCESS; +} + +kern_return_t +x_bootstrap_look_up(mach_port_t bp, audit_token_t au_tok, name_t servicename, mach_port_t *serviceportp, mach_msg_type_name_t *ptype) +{ + struct jobcb *j = job_find_by_port(bp); + struct machservice *ms; + struct ldcred ldc; + + audit_token_to_launchd_cred(au_tok, &ldc); + + trusted_client_check(j, &ldc); + + ms = job_lookup_service(j, servicename, true); + + if (ms && machservice_hidden(ms) && !job_active(machservice_job(ms))) { + ms = NULL; + } + + if (ms) { + launchd_assumes(machservice_port(ms) != MACH_PORT_NULL); + job_log(j, LOG_DEBUG, "Mach service lookup (by PID %d): %s", ldc.pid, servicename); + *serviceportp = machservice_port(ms); + *ptype = MACH_MSG_TYPE_COPY_SEND; + return BOOTSTRAP_SUCCESS; + } else if (inherited_bootstrap_port != MACH_PORT_NULL) { + job_log(j, LOG_DEBUG, "Mach service lookup (by PID %d) forwarded: %s", ldc.pid, servicename); + *ptype = MACH_MSG_TYPE_MOVE_SEND; + return bootstrap_look_up(inherited_bootstrap_port, servicename, serviceportp); + } else { + job_log(j, LOG_DEBUG, "Mach service lookup (by PID %d) failed: %s", ldc.pid, servicename); + return BOOTSTRAP_UNKNOWN_SERVICE; + } +} + +kern_return_t +x_bootstrap_parent(mach_port_t bp, mach_port_t *parentport, mach_msg_type_name_t *pptype) +{ + struct jobcb *j = job_find_by_port(bp); + + job_log(j, LOG_DEBUG, "Requested parent bootstrap port"); + + j = job_get_bs(j); + + *pptype = MACH_MSG_TYPE_MAKE_SEND; + + if (job_parent(j)) { + *parentport = job_get_bsport(job_parent(j)); + } else if (MACH_PORT_NULL == inherited_bootstrap_port) { + *parentport = job_get_bsport(j); + } else { + *pptype = MACH_MSG_TYPE_COPY_SEND; + *parentport = inherited_bootstrap_port; + } + return BOOTSTRAP_SUCCESS; +} + +static void +x_bootstrap_info_countservices(struct machservice *ms, void *context) +{ + unsigned int *cnt = context; + + (*cnt)++; +} + +struct x_bootstrap_info_copyservices_cb { + name_array_t service_names; + bootstrap_status_array_t service_actives; + mach_port_array_t ports; + unsigned int i; +}; + +static void +x_bootstrap_info_copyservices(struct machservice *ms, void *context) +{ + struct x_bootstrap_info_copyservices_cb *info_resp = context; + + strlcpy(info_resp->service_names[info_resp->i], machservice_name(ms), sizeof(info_resp->service_names[0])); + + launchd_assumes(info_resp->service_actives || info_resp->ports); + + if (info_resp->service_actives) { + info_resp->service_actives[info_resp->i] = machservice_status(ms); + } else { + info_resp->ports[info_resp->i] = machservice_port(ms); + } + info_resp->i++; +} + +kern_return_t +x_bootstrap_info(mach_port_t bp, name_array_t *servicenamesp, unsigned int *servicenames_cnt, + bootstrap_status_array_t *serviceactivesp, unsigned int *serviceactives_cnt) +{ + struct x_bootstrap_info_copyservices_cb info_resp = { NULL, NULL, NULL, 0 }; + struct jobcb *ji, *j = job_find_by_port(bp); + kern_return_t result; + unsigned int cnt = 0; + + for (ji = j; ji; ji = job_parent(ji)) + job_foreach_service(ji, x_bootstrap_info_countservices, &cnt, true); + + result = vm_allocate(mach_task_self(), (vm_address_t *)&info_resp.service_names, cnt * sizeof(info_resp.service_names[0]), true); + if (!launchd_assumes(result == KERN_SUCCESS)) + goto out_bad; + + result = vm_allocate(mach_task_self(), (vm_address_t *)&info_resp.service_actives, cnt * sizeof(info_resp.service_actives[0]), true); + if (!launchd_assumes(result == KERN_SUCCESS)) + goto out_bad; + + for (ji = j; ji; ji = job_parent(ji)) + job_foreach_service(ji, x_bootstrap_info_copyservices, &info_resp, true); + + launchd_assumes(info_resp.i == cnt); + + *servicenamesp = info_resp.service_names; + *serviceactivesp = info_resp.service_actives; + *servicenames_cnt = *serviceactives_cnt = cnt; + + return BOOTSTRAP_SUCCESS; + +out_bad: + if (info_resp.service_names) + vm_deallocate(mach_task_self(), (vm_address_t)info_resp.service_names, cnt * sizeof(info_resp.service_names[0])); + + return BOOTSTRAP_NO_MEMORY; +} + +kern_return_t +x_bootstrap_transfer_subset(mach_port_t bp, mach_port_t *reqport, mach_port_t *rcvright, + name_array_t *servicenamesp, unsigned int *servicenames_cnt, + mach_port_array_t *ports, unsigned int *ports_cnt) +{ + struct x_bootstrap_info_copyservices_cb info_resp = { NULL, NULL, NULL, 0 }; + struct jobcb *j = job_find_by_port(bp); + unsigned int cnt = 0; + kern_return_t result; + + if (getpid() != 1) { + job_log(j, LOG_ERR, "Only the system launchd will transfer Mach sub-bootstraps."); + return BOOTSTRAP_NOT_PRIVILEGED; + } else if (!job_parent(j)) { + job_log(j, LOG_ERR, "Root Mach bootstrap cannot be transferred."); + return BOOTSTRAP_NOT_PRIVILEGED; + } + + job_log(j, LOG_DEBUG, "Transferring sub-bootstrap to the per session launchd."); + + job_foreach_service(j, x_bootstrap_info_countservices, &cnt, false); + + result = vm_allocate(mach_task_self(), (vm_address_t *)&info_resp.service_names, cnt * sizeof(info_resp.service_names[0]), true); + if (!launchd_assumes(result == KERN_SUCCESS)) + goto out_bad; + + result = vm_allocate(mach_task_self(), (vm_address_t *)&info_resp.ports, cnt * sizeof(info_resp.ports[0]), true); + if (!launchd_assumes(result == KERN_SUCCESS)) + goto out_bad; + + job_foreach_service(j, x_bootstrap_info_copyservices, &info_resp, false); + + launchd_assumes(info_resp.i == cnt); + + *servicenamesp = info_resp.service_names; + *ports = info_resp.ports; + *servicenames_cnt = *ports_cnt = cnt; + + *reqport = job_get_reqport(j); + *rcvright = job_get_bsport(j); + + launchd_assumes(launchd_mport_request_callback(*rcvright, NULL, true) == KERN_SUCCESS); + + launchd_assumes(launchd_mport_make_send(*rcvright) == KERN_SUCCESS); + + return BOOTSTRAP_SUCCESS; + +out_bad: + if (info_resp.service_names) + vm_deallocate(mach_task_self(), (vm_address_t)info_resp.service_names, cnt * sizeof(info_resp.service_names[0])); + + return BOOTSTRAP_NO_MEMORY; +} + +kern_return_t +x_bootstrap_subset(mach_port_t bp, mach_port_t requestorport, mach_port_t *subsetportp) +{ + struct jobcb *js, *j = job_find_by_port(bp); + int bsdepth = 0; + + while ((j = job_parent(j)) != NULL) + bsdepth++; + + j = job_find_by_port(bp); + + /* Since we use recursion, we need an artificial depth for subsets */ + if (bsdepth > 100) { + job_log(j, LOG_ERR, "Mach sub-bootstrap create request failed. Depth greater than: %d", bsdepth); + return BOOTSTRAP_NO_MEMORY; + } + + if ((js = job_new_bootstrap(j, requestorport, MACH_PORT_NULL)) == NULL) { + if (requestorport == MACH_PORT_NULL) + return BOOTSTRAP_NOT_PRIVILEGED; + return BOOTSTRAP_NO_MEMORY; + } + + *subsetportp = job_get_bsport(js); + return BOOTSTRAP_SUCCESS; +} + +kern_return_t +x_bootstrap_create_service(mach_port_t bp, name_t servicename, mach_port_t *serviceportp) +{ + struct jobcb *j = job_find_by_port(bp); + struct machservice *ms; + + if (job_prog(j)[0] == '\0') { + job_log(j, LOG_ERR, "Mach service creation requires a target server: %s", servicename); + return BOOTSTRAP_NOT_PRIVILEGED; + } + + ms = job_lookup_service(j, servicename, false); + if (ms) { + job_log(j, LOG_DEBUG, "Mach service creation attempt for failed. Already exists: %s", servicename); + return BOOTSTRAP_NAME_IN_USE; + } + + job_checkin(j); + + *serviceportp = MACH_PORT_NULL; + ms = machservice_new(j, servicename, serviceportp); + + if (!launchd_assumes(ms != NULL)) + goto out_bad; + + return BOOTSTRAP_SUCCESS; + +out_bad: + launchd_assumes(launchd_mport_close_recv(*serviceportp) == KERN_SUCCESS); + return BOOTSTRAP_NO_MEMORY; +} + +kern_return_t +x_mpm_wait(mach_port_t bp, mach_port_t srp, audit_token_t au_tok, integer_t *waitstatus) +{ + struct jobcb *j = job_find_by_port(bp); +#if 0 + struct ldcred ldc; + audit_token_to_launchd_cred(au_tok, &ldc); +#endif + return job_handle_mpm_wait(j, srp, waitstatus); +} + +kern_return_t +x_mpm_uncork_fork(mach_port_t bp, audit_token_t au_tok) +{ + struct jobcb *j = job_find_by_port(bp); + + if (!j) + return BOOTSTRAP_NOT_PRIVILEGED; + + job_uncork_fork(j); + + return 0; +} + +kern_return_t +x_mpm_spawn(mach_port_t bp, audit_token_t au_tok, + _internal_string_t charbuf, mach_msg_type_number_t charbuf_cnt, + uint32_t argc, uint32_t envc, uint64_t flags, uint16_t mig_umask, + pid_t *child_pid, mach_port_t *obsvr_port) +{ + struct jobcb *jr, *j = job_find_by_port(bp); + struct ldcred ldc; + size_t offset = 0; + char *tmpp; + const char **argv = NULL, **env = NULL; + const char *label = NULL; + const char *path = NULL; + const char *workingdir = NULL; + size_t argv_i = 0, env_i = 0; + + audit_token_to_launchd_cred(au_tok, &ldc); + +#if 0 + if (ldc.asid != inherited_asid) { + job_log(j, LOG_ERR, "Security: PID %d (ASID %d) was denied a request to spawn a process in this session (ASID %d)", + ldc.pid, ldc.asid, inherited_asid); + return BOOTSTRAP_NOT_PRIVILEGED; + } +#endif + + argv = alloca((argc + 1) * sizeof(char *)); + memset(argv, 0, (argc + 1) * sizeof(char *)); + + if (envc > 0) { + env = alloca((envc + 1) * sizeof(char *)); + memset(env, 0, (envc + 1) * sizeof(char *)); + } + + while (offset < charbuf_cnt) { + tmpp = charbuf + offset; + offset += strlen(tmpp) + 1; + if (!label) { + label = tmpp; + } else if (argc > 0) { + argv[argv_i] = tmpp; + argv_i++; + argc--; + } else if (envc > 0) { + env[env_i] = tmpp; + env_i++; + envc--; + } else if (flags & SPAWN_HAS_PATH) { + path = tmpp; + flags &= ~SPAWN_HAS_PATH; + } else if (flags & SPAWN_HAS_WDIR) { + workingdir = tmpp; + flags &= ~SPAWN_HAS_WDIR; + } + } + + jr = job_new_spawn(label, path, workingdir, argv, env, flags & SPAWN_HAS_UMASK ? &mig_umask : NULL, + flags & SPAWN_WANTS_WAIT4DEBUGGER, flags & SPAWN_WANTS_FORCE_PPC); + + if (jr == NULL) switch (errno) { + case EEXIST: + return BOOTSTRAP_NAME_IN_USE; + default: + return BOOTSTRAP_NO_MEMORY; + } + + job_log(j, LOG_INFO, "Spawned with flags:%s%s", + flags & SPAWN_WANTS_FORCE_PPC ? " ppc": "", + flags & SPAWN_WANTS_WAIT4DEBUGGER ? " stopped": ""); + + *child_pid = job_get_pid(jr); + *obsvr_port = job_get_bsport(jr); + + return BOOTSTRAP_SUCCESS; +} + +kern_return_t +do_mach_notify_port_destroyed(mach_port_t notify, mach_port_t rights) +{ + /* This message is sent to us when a receive right is returned to us. */ + + if (!job_ack_port_destruction(root_job, rights)) { + launchd_assumes(launchd_mport_close_recv(rights) == KERN_SUCCESS); + } + + return KERN_SUCCESS; +} + +kern_return_t +do_mach_notify_port_deleted(mach_port_t notify, mach_port_name_t name) +{ + /* If we deallocate/destroy/mod_ref away a port with a pending notification, + * the original notification message is replaced with this message. + * + * To quote a Mach kernel expert, "the kernel has a send-once right that has + * to be used somehow." + */ + return KERN_SUCCESS; +} + +kern_return_t +do_mach_notify_no_senders(mach_port_t notify, mach_port_mscount_t mscount) +{ + struct jobcb *j = job_find_by_port(notify); + + /* This message is sent to us when the last customer of one of our objects + * goes away. + */ + + if (!launchd_assumes(j != NULL)) + return KERN_FAILURE; + + job_ack_no_senders(j); + + return KERN_SUCCESS; +} + +kern_return_t +do_mach_notify_send_once(mach_port_t notify) +{ + /* + * This message is sent to us every time we close a port that we have + * outstanding Mach notification requests on. We can safely ignore + * this message. + */ + return KERN_SUCCESS; +} + +kern_return_t +do_mach_notify_dead_name(mach_port_t notify, mach_port_name_t name) +{ + /* This message is sent to us when one of our send rights no longer has + * a receiver somewhere else on the system. + */ + + if (name == inherited_bootstrap_port) { + launchd_assumes(launchd_mport_deallocate(name) == KERN_SUCCESS); + inherited_bootstrap_port = MACH_PORT_NULL; + } + + job_delete_anything_with_port(root_job, name); + + /* A dead-name notification about a port appears to increment the + * rights on said port. Let's deallocate it so that we don't leak + * dead-name ports. + */ + launchd_assumes(launchd_mport_deallocate(name) == KERN_SUCCESS); + + return KERN_SUCCESS; +} + +bool +trusted_client_check(struct jobcb *j, struct ldcred *ldc) +{ + static pid_t last_warned_pid = 0; + + /* In the long run, we wish to enforce the progeny rule, but for now, + * we'll let root and the user be forgiven. Once we get CoreProcesses + * to switch to using launchd rather than the WindowServer for indirect + * process invocation, we can then seriously look at cranking up the + * warning level here. + */ + + if (inherited_asid == ldc->asid) + return true; + if (progeny_check(ldc->pid)) + return true; + if (ldc->euid == geteuid()) + return true; + if (ldc->euid == 0 && ldc->uid == 0) + return true; + if (last_warned_pid == ldc->pid) + return false; + + job_log(j, LOG_NOTICE, "Security: PID %d (ASID %d) was leaked into this session (ASID %d). This will be denied in the future.", ldc->pid, ldc->asid, inherited_asid); + + last_warned_pid = ldc->pid; + + return false; +} diff --git a/launchd/src/launchd_unix_ipc.c b/launchd/src/launchd_unix_ipc.c new file mode 100644 index 0000000..712a124 --- /dev/null +++ b/launchd/src/launchd_unix_ipc.c @@ -0,0 +1,523 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_APACHE_LICENSE_HEADER_START@ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @APPLE_APACHE_LICENSE_HEADER_END@ + */ + +static const char *const __rcs_file_version__ = "$Revision: 1.11 $"; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "launch.h" +#include "launch_priv.h" +#include "launchd.h" +#include "launchd_core_logic.h" +#include "launchd_unix_ipc.h" + +extern char **environ; + +static SLIST_HEAD(, conncb) connections = { NULL }; + +static launch_data_t adjust_rlimits(launch_data_t in); + +static void ipc_readmsg2(launch_data_t data, const char *cmd, void *context); + +static void ipc_listen_callback(void *obj __attribute__((unused)), struct kevent *kev); + +static kq_callback kqipc_listen_callback = ipc_listen_callback; + +static pid_t ipc_self = 0; + +char *sockpath = NULL; +static char *sockdir = NULL; + +static bool ipc_inited = false; + +void +ipc_clean_up(void) +{ + if (ipc_self != getpid()) + return; + + if (-1 == unlink(sockpath)) + syslog(LOG_WARNING, "unlink(\"%s\"): %m", sockpath); + else if (-1 == rmdir(sockdir)) + syslog(LOG_WARNING, "rmdir(\"%s\"): %m", sockdir); +} + +void +ipc_server_init(int *fds, size_t fd_cnt) +{ + struct sockaddr_un sun; + mode_t oldmask; + int r, fd = -1; + char ourdir[1024]; + size_t i; + + if (ipc_inited) + return; + + if (fds) + goto add_fds; + + memset(&sun, 0, sizeof(sun)); + sun.sun_family = AF_UNIX; + + if (getpid() == 1) { + strcpy(ourdir, LAUNCHD_SOCK_PREFIX); + strncpy(sun.sun_path, LAUNCHD_SOCK_PREFIX "/sock", sizeof(sun.sun_path)); + + unlink(ourdir); + if (mkdir(ourdir, S_IRWXU) == -1) { + if (errno == EROFS) { + goto out_bad; + } else if (errno == EEXIST) { + struct stat sb; + stat(ourdir, &sb); + if (!S_ISDIR(sb.st_mode)) { + errno = EEXIST; + syslog(LOG_ERR, "mkdir(\"%s\"): %m", LAUNCHD_SOCK_PREFIX); + goto out_bad; + } + } else { + syslog(LOG_ERR, "mkdir(\"%s\"): %m", ourdir); + goto out_bad; + } + } + } else { + snprintf(ourdir, sizeof(ourdir), "/tmp/launchd-%u.XXXXXX", getpid()); + if (!launchd_assumes(mkdtemp(ourdir) != NULL)) + goto out_bad; + snprintf(sun.sun_path, sizeof(sun.sun_path), "%s/sock", ourdir); + setenv(LAUNCHD_SOCKET_ENV, sun.sun_path, 1); + } + + if (unlink(sun.sun_path) == -1 && errno != ENOENT) { + if (errno != EROFS) + syslog(LOG_ERR, "unlink(\"thesocket\"): %m"); + goto out_bad; + } + + if (!launchd_assumes((fd = _fd(socket(AF_UNIX, SOCK_STREAM, 0))) != -1)) + goto out_bad; + + oldmask = umask(S_IRWXG|S_IRWXO); + r = bind(fd, (struct sockaddr *)&sun, sizeof(sun)); + umask(oldmask); + + if (r == -1) { + if (errno != EROFS) + syslog(LOG_ERR, "bind(\"thesocket\"): %m"); + goto out_bad; + } + + if (listen(fd, SOMAXCONN) == -1) { + syslog(LOG_ERR, "listen(\"thesocket\"): %m"); + goto out_bad; + } + +add_fds: + if (fds) { + for (i = 0; i < fd_cnt; i++) { + if (kevent_mod(fds[i], EVFILT_READ, EV_ADD, 0, 0, &kqipc_listen_callback) == -1) { + syslog(LOG_ERR, "kevent_mod(%d, EVFILT_READ): %m", fds[i]); + goto out_bad; + } + } + } else if (kevent_mod(fd, EVFILT_READ, EV_ADD, 0, 0, &kqipc_listen_callback) == -1) { + syslog(LOG_ERR, "kevent_mod(\"thesocket\", EVFILT_READ): %m"); + goto out_bad; + } + + ipc_inited = true; + + if (!fds) { + sockdir = strdup(ourdir); + sockpath = strdup(sun.sun_path); + ipc_self = getpid(); + atexit(ipc_clean_up); + } + +out_bad: + if (!ipc_inited && fd != -1) + launchd_assumes(close(fd) == 0); +} + +void +ipc_open(int fd, struct jobcb *j) +{ + struct conncb *c = calloc(1, sizeof(struct conncb)); + + fcntl(fd, F_SETFL, O_NONBLOCK); + + c->kqconn_callback = ipc_callback; + c->conn = launchd_fdopen(fd); + c->j = j; + SLIST_INSERT_HEAD(&connections, c, sle); + kevent_mod(fd, EVFILT_READ, EV_ADD, 0, 0, &c->kqconn_callback); +} + +void +ipc_listen_callback(void *obj __attribute__((unused)), struct kevent *kev) +{ + struct sockaddr_un sun; + socklen_t sl = sizeof(sun); + int cfd; + + if ((cfd = _fd(accept(kev->ident, (struct sockaddr *)&sun, &sl))) == -1) { + return; + } + + ipc_open(cfd, NULL); +} + +void +ipc_callback(void *obj, struct kevent *kev) +{ + struct conncb *c = obj; + int r; + + if (kev->filter == EVFILT_READ) { + if (launchd_msg_recv(c->conn, ipc_readmsg, c) == -1 && errno != EAGAIN) { + if (errno != ECONNRESET) + syslog(LOG_DEBUG, "%s(): recv: %m", __func__); + ipc_close(c); + } + } else if (kev->filter == EVFILT_WRITE) { + r = launchd_msg_send(c->conn, NULL); + if (r == -1) { + if (errno != EAGAIN) { + syslog(LOG_DEBUG, "%s(): send: %m", __func__); + ipc_close(c); + } + } else if (r == 0) { + kevent_mod(launchd_getfd(c->conn), EVFILT_WRITE, EV_DELETE, 0, 0, NULL); + } + } else { + syslog(LOG_DEBUG, "%s(): unknown filter type!", __func__); + ipc_close(c); + } +} + +static void set_user_env(launch_data_t obj, const char *key, void *context __attribute__((unused))) +{ + setenv(key, launch_data_get_string(obj), 1); +} + +void +ipc_close_fds(launch_data_t o) +{ + size_t i; + + switch (launch_data_get_type(o)) { + case LAUNCH_DATA_DICTIONARY: + launch_data_dict_iterate(o, (void (*)(launch_data_t, const char *, void *))ipc_close_fds, NULL); + break; + case LAUNCH_DATA_ARRAY: + for (i = 0; i < launch_data_array_get_count(o); i++) + ipc_close_fds(launch_data_array_get_index(o, i)); + break; + case LAUNCH_DATA_FD: + if (launch_data_get_fd(o) != -1) + launchd_assumes(close(launch_data_get_fd(o)) == 0); + break; + default: + break; + } +} + +void +ipc_revoke_fds(launch_data_t o) +{ + size_t i; + + switch (launch_data_get_type(o)) { + case LAUNCH_DATA_DICTIONARY: + launch_data_dict_iterate(o, (void (*)(launch_data_t, const char *, void *))ipc_revoke_fds, NULL); + break; + case LAUNCH_DATA_ARRAY: + for (i = 0; i < launch_data_array_get_count(o); i++) + ipc_revoke_fds(launch_data_array_get_index(o, i)); + break; + case LAUNCH_DATA_FD: + launch_data_set_fd(o, -1); + break; + default: + break; + } +} + +struct readmsg_context { + struct conncb *c; + launch_data_t resp; +}; + +void +ipc_readmsg(launch_data_t msg, void *context) +{ + struct readmsg_context rmc = { context, NULL }; + + if (LAUNCH_DATA_DICTIONARY == launch_data_get_type(msg)) { + launch_data_dict_iterate(msg, ipc_readmsg2, &rmc); + } else if (LAUNCH_DATA_STRING == launch_data_get_type(msg)) { + ipc_readmsg2(NULL, launch_data_get_string(msg), &rmc); + } else { + rmc.resp = launch_data_new_errno(EINVAL); + } + + if (NULL == rmc.resp) + rmc.resp = launch_data_new_errno(ENOSYS); + + ipc_close_fds(msg); + + if (launchd_msg_send(rmc.c->conn, rmc.resp) == -1) { + if (errno == EAGAIN) { + kevent_mod(launchd_getfd(rmc.c->conn), EVFILT_WRITE, EV_ADD, 0, 0, &rmc.c->kqconn_callback); + } else { + syslog(LOG_DEBUG, "launchd_msg_send() == -1: %m"); + ipc_close(rmc.c); + } + } + launch_data_free(rmc.resp); +} + + +void +ipc_readmsg2(launch_data_t data, const char *cmd, void *context) +{ + struct readmsg_context *rmc = context; + launch_data_t resp = NULL; + struct jobcb *j; + + if (rmc->resp) + return; + + if (data == NULL) { + if (!strcmp(cmd, LAUNCH_KEY_CHECKIN)) { + if (rmc->c->j) { + resp = job_export(rmc->c->j); + job_checkin(rmc->c->j); + } else { + resp = launch_data_new_errno(EACCES); + } + } else if (!strcmp(cmd, LAUNCH_KEY_RELOADTTYS)) { + update_ttys(); + resp = launch_data_new_errno(0); + } else if (!strcmp(cmd, LAUNCH_KEY_SHUTDOWN)) { + launchd_shutdown(); + resp = launch_data_new_errno(0); + } else if (!strcmp(cmd, LAUNCH_KEY_SINGLEUSER)) { + launchd_single_user(); + resp = launch_data_new_errno(0); + } else if (!strcmp(cmd, LAUNCH_KEY_GETJOBS)) { + resp = job_export_all(); + ipc_revoke_fds(resp); + } else if (!strcmp(cmd, LAUNCH_KEY_GETRESOURCELIMITS)) { + resp = adjust_rlimits(NULL); + } else if (!strcmp(cmd, LAUNCH_KEY_GETUSERENVIRONMENT)) { + char **tmpenviron = environ; + resp = launch_data_alloc(LAUNCH_DATA_DICTIONARY); + for (; *tmpenviron; tmpenviron++) { + char envkey[1024]; + launch_data_t s = launch_data_alloc(LAUNCH_DATA_STRING); + launch_data_set_string(s, strchr(*tmpenviron, '=') + 1); + strncpy(envkey, *tmpenviron, sizeof(envkey)); + *(strchr(envkey, '=')) = '\0'; + launch_data_dict_insert(resp, s, envkey); + } + } else if (!strcmp(cmd, LAUNCH_KEY_GETLOGMASK)) { + int oldmask = setlogmask(LOG_UPTO(LOG_DEBUG)); + resp = launch_data_new_integer(oldmask); + setlogmask(oldmask); + } else if (!strcmp(cmd, LAUNCH_KEY_GETUMASK)) { + mode_t oldmask = umask(0); + resp = launch_data_new_integer(oldmask); + umask(oldmask); + } else if (!strcmp(cmd, LAUNCH_KEY_GETRUSAGESELF)) { + struct rusage rusage; + getrusage(RUSAGE_SELF, &rusage); + resp = launch_data_new_opaque(&rusage, sizeof(rusage)); + } else if (!strcmp(cmd, LAUNCH_KEY_GETRUSAGECHILDREN)) { + struct rusage rusage; + getrusage(RUSAGE_CHILDREN, &rusage); + resp = launch_data_new_opaque(&rusage, sizeof(rusage)); + } else if (!strcmp(cmd, LAUNCH_KEY_BATCHQUERY)) { + resp = launch_data_alloc(LAUNCH_DATA_BOOL); + launch_data_set_bool(resp, batch_disabler_count == 0); + } + } else if (!strcmp(cmd, LAUNCH_KEY_STARTJOB)) { + if ((j = job_find(root_job, launch_data_get_string(data))) != NULL) { + job_start(j); + errno = 0; + } + resp = launch_data_new_errno(errno); + } else if (!strcmp(cmd, LAUNCH_KEY_STOPJOB)) { + if ((j = job_find(root_job, launch_data_get_string(data))) != NULL) { + job_stop(j); + errno = 0; + } + resp = launch_data_new_errno(errno); + } else if (!strcmp(cmd, LAUNCH_KEY_REMOVEJOB)) { + if ((j = job_find(root_job, launch_data_get_string(data))) != NULL) { + job_remove(j); + errno = 0; + } + resp = launch_data_new_errno(errno); + } else if (!strcmp(cmd, LAUNCH_KEY_SUBMITJOB)) { + if (launch_data_get_type(data) == LAUNCH_DATA_ARRAY) { + resp = job_import_bulk(data); + } else { + if (job_import(data)) + errno = 0; + resp = launch_data_new_errno(errno); + } + } else if (!strcmp(cmd, LAUNCH_KEY_UNSETUSERENVIRONMENT)) { + unsetenv(launch_data_get_string(data)); + resp = launch_data_new_errno(0); + } else if (!strcmp(cmd, LAUNCH_KEY_SETUSERENVIRONMENT)) { + launch_data_dict_iterate(data, set_user_env, NULL); + resp = launch_data_new_errno(0); + } else if (!strcmp(cmd, LAUNCH_KEY_SETRESOURCELIMITS)) { + resp = adjust_rlimits(data); + } else if (!strcmp(cmd, LAUNCH_KEY_GETJOB)) { + if ((j = job_find(root_job, launch_data_get_string(data))) == NULL) { + resp = launch_data_new_errno(errno); + } else { + resp = job_export(j); + ipc_revoke_fds(resp); + } + } else if (!strcmp(cmd, LAUNCH_KEY_GETJOBWITHHANDLES)) { + if ((j = job_find(root_job, launch_data_get_string(data))) == NULL) { + resp = launch_data_new_errno(errno); + } else { + resp = job_export(j); + } + } else if (!strcmp(cmd, LAUNCH_KEY_SETLOGMASK)) { + resp = launch_data_new_integer(setlogmask(launch_data_get_integer(data))); + } else if (!strcmp(cmd, LAUNCH_KEY_SETUMASK)) { + resp = launch_data_new_integer(umask(launch_data_get_integer(data))); + } else if (!strcmp(cmd, LAUNCH_KEY_SETSTDOUT)) { + resp = launchd_setstdio(STDOUT_FILENO, data); + } else if (!strcmp(cmd, LAUNCH_KEY_SETSTDERR)) { + resp = launchd_setstdio(STDERR_FILENO, data); + } else if (!strcmp(cmd, LAUNCH_KEY_BATCHCONTROL)) { + batch_job_enable(launch_data_get_bool(data), rmc->c); + resp = launch_data_new_errno(0); + } + + rmc->resp = resp; +} + +void +ipc_close(struct conncb *c) +{ + batch_job_enable(true, c); + + SLIST_REMOVE(&connections, c, conncb, sle); + launchd_close(c->conn); + free(c); +} + +launch_data_t +adjust_rlimits(launch_data_t in) +{ + struct rlimit l[RLIM_NLIMITS]; + struct rlimit *ltmp; + size_t i,ltmpsz; + + for (i = 0; i < RLIM_NLIMITS; i++) { + launchd_assumes(getrlimit(i, l + i) != -1); + } + + if (in) { + ltmp = launch_data_get_opaque(in); + ltmpsz = launch_data_get_opaque_size(in); + + if (ltmpsz > sizeof(l)) { + syslog(LOG_WARNING, "Too much rlimit data sent!"); + ltmpsz = sizeof(l); + } + + for (i = 0; i < (ltmpsz / sizeof(struct rlimit)); i++) { + if (ltmp[i].rlim_cur == l[i].rlim_cur && ltmp[i].rlim_max == l[i].rlim_max) + continue; + + if (/* XXX readcfg_pid && */ getpid() == 1) { + int gmib[] = { CTL_KERN, KERN_MAXPROC }; + int pmib[] = { CTL_KERN, KERN_MAXPROCPERUID }; + const char *gstr = "kern.maxproc"; + const char *pstr = "kern.maxprocperuid"; + int gval = ltmp[i].rlim_max; + int pval = ltmp[i].rlim_cur; + switch (i) { + case RLIMIT_NOFILE: + gmib[1] = KERN_MAXFILES; + pmib[1] = KERN_MAXFILESPERPROC; + gstr = "kern.maxfiles"; + pstr = "kern.maxfilesperproc"; + break; + case RLIMIT_NPROC: + /* kernel will not clamp to this value, we must */ + if (gval > (2048 + 20)) + gval = 2048 + 20; + break; + default: + break; + } + + if (gval > 0) { + launchd_assumes(sysctl(gmib, 2, NULL, NULL, &gval, sizeof(gval)) != -1); + } else { + syslog(LOG_WARNING, "sysctl(\"%s\"): can't be zero", gstr); + } + if (pval > 0) { + launchd_assumes(sysctl(pmib, 2, NULL, NULL, &pval, sizeof(pval)) != -1); + } else { + syslog(LOG_WARNING, "sysctl(\"%s\"): can't be zero", pstr); + } + } + launchd_assumes(setrlimit(i, ltmp + i) != -1); + /* the kernel may have clamped the values we gave it */ + launchd_assumes(getrlimit(i, l + i) != -1); + } + } + + return launch_data_new_opaque(l, sizeof(struct rlimit) * RLIM_NLIMITS); +} diff --git a/launchd/src/launchd_unix_ipc.h b/launchd/src/launchd_unix_ipc.h new file mode 100644 index 0000000..82440fd --- /dev/null +++ b/launchd/src/launchd_unix_ipc.h @@ -0,0 +1,42 @@ +#ifndef __LAUNCHD_UNIX_IPC__ +#define __LAUNCHD_UNIX_IPC__ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_APACHE_LICENSE_HEADER_START@ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @APPLE_APACHE_LICENSE_HEADER_END@ + */ + +struct conncb { + kq_callback kqconn_callback; + SLIST_ENTRY(conncb) sle; + launch_t conn; + struct jobcb *j; + int disabled_batch:1, futureflags:31; +}; + +extern char *sockpath; + +void ipc_open(int fd, struct jobcb *j); +void ipc_close(struct conncb *c); +void ipc_callback(void *, struct kevent *); +void ipc_readmsg(launch_data_t msg, void *context); +void ipc_revoke_fds(launch_data_t o); +void ipc_close_fds(launch_data_t o); +void ipc_clean_up(void); +void ipc_server_init(int *, size_t); + +#endif diff --git a/launchd/src/launchdebugd.c b/launchd/src/launchdebugd.c deleted file mode 100644 index 3c3d238..0000000 --- a/launchd/src/launchdebugd.c +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The 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, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "launch.h" - -static void launch_print_obj(launch_data_t o, FILE *w); - -static int kq = -1; - -static void find_fds(launch_data_t o, const char *key __attribute__((unused)), void *context __attribute__((unused))) -{ - struct kevent kev; - size_t i; - - switch (launch_data_get_type(o)) { - case LAUNCH_DATA_FD: - EV_SET(&kev, launch_data_get_fd(o), EVFILT_READ, EV_ADD, 0, 0, NULL); - if (kevent(kq, &kev, 1, NULL, 0, NULL) == -1) - syslog(LOG_DEBUG, "kevent(): %m"); - break; - case LAUNCH_DATA_ARRAY: - for (i = 0; i < launch_data_array_get_count(o); i++) - find_fds(launch_data_array_get_index(o, i), NULL, NULL); - break; - case LAUNCH_DATA_DICTIONARY: - launch_data_dict_iterate(o, find_fds, NULL); - break; - default: - break; - } -} - -int main(void) -{ - int r; - struct sockaddr_storage ss; - socklen_t slen = sizeof(ss); - struct kevent kev; - FILE *c; - launch_data_t tmp, resp, msg = launch_data_alloc(LAUNCH_DATA_STRING); - - kq = kqueue(); - - launch_data_set_string(msg, LAUNCH_KEY_CHECKIN); - - openlog(getprogname(), LOG_PERROR|LOG_PID|LOG_CONS, LOG_DAEMON); - - if ((resp = launch_msg(msg)) == NULL) { - syslog(LOG_ERR, "launch_msg(\"" LAUNCH_KEY_CHECKIN "\"): %m"); - exit(EXIT_FAILURE); - } - - tmp = launch_data_dict_lookup(resp, LAUNCH_JOBKEY_SOCKETS); - if (tmp) { - find_fds(tmp, NULL, NULL); - } else { - syslog(LOG_ERR, "No FDs found to answer requests on!"); - exit(EXIT_FAILURE); - } - - launch_data_free(resp); - - if ((r = kevent(kq, NULL, 0, &kev, 1, NULL)) == -1) { - syslog(LOG_ERR, "kevent(): %m"); - exit(EXIT_FAILURE); - } else if (r == 0) { - exit(EXIT_SUCCESS); - } - if ((r = accept(kev.ident, (struct sockaddr *)&ss, &slen)) == -1) { - syslog(LOG_ERR, "accept(): %m"); - exit(EXIT_FAILURE); - } - - c = fdopen(r, "r+"); - - fprintf(c, "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n"); - - fprintf(c, "\n\n"); - - launch_data_set_string(msg, LAUNCH_KEY_GETJOBS); - - resp = launch_msg(msg); - - launch_print_obj(resp, c); - - fprintf(c, "\n\n"); - - fclose(c); - - exit(EXIT_SUCCESS); -} - -static void launch_print_obj_dict_callback(launch_data_t obj, const char *key, void *context) -{ - FILE *w = context; - - fprintf(w, "%s\n", key); - if (launch_data_get_type(obj) != LAUNCH_DATA_ARRAY && - launch_data_get_type(obj) != LAUNCH_DATA_DICTIONARY) - fprintf(w, "
  • \n"); - launch_print_obj(obj, w); - if (launch_data_get_type(obj) != LAUNCH_DATA_ARRAY && - launch_data_get_type(obj) != LAUNCH_DATA_DICTIONARY) - fprintf(w, "
\n"); -} - -static void launch_print_obj(launch_data_t o, FILE *w) -{ - size_t i; - - - switch (launch_data_get_type(o)) { - case LAUNCH_DATA_DICTIONARY: - fprintf(w, "
  • \n"); - launch_data_dict_iterate(o, launch_print_obj_dict_callback, w); - fprintf(w, "
\n"); - break; - case LAUNCH_DATA_ARRAY: - fprintf(w, "
    \n"); - for (i = 0; i < launch_data_array_get_count(o); i++) { - fprintf(w, "
  1. "); - launch_print_obj(launch_data_array_get_index(o, i), w); - fprintf(w, "
  2. \n"); - } - fprintf(w, "
\n"); - break; - case LAUNCH_DATA_INTEGER: - fprintf(w, "Number: %lld", launch_data_get_integer(o)); - break; - case LAUNCH_DATA_REAL: - fprintf(w, "Float: %f", launch_data_get_real(o)); - break; - case LAUNCH_DATA_STRING: - fprintf(w, "String: %s", launch_data_get_string(o)); - break; - case LAUNCH_DATA_OPAQUE: - fprintf(w, "Opaque: %p size %zu", launch_data_get_opaque(o), launch_data_get_opaque_size(o)); - break; - case LAUNCH_DATA_FD: - fprintf(w, "FD: %d", launch_data_get_fd(o)); - break; - case LAUNCH_DATA_BOOL: - fprintf(w, "Bool: %s", launch_data_get_bool(o) ? "true" : "false"); - break; - default: - fprintf(w, "type %d is unknown", launch_data_get_type(o)); - break; - } -} diff --git a/launchd/src/launchproxy.c b/launchd/src/launchproxy.c index 3149b37..039c81a 100644 --- a/launchd/src/launchproxy.c +++ b/launchd/src/launchproxy.c @@ -1,24 +1,21 @@ /* * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ + * @APPLE_APACHE_LICENSE_HEADER_START@ * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * The 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, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and * limitations under the License. * - * @APPLE_LICENSE_HEADER_END@ + * @APPLE_APACHE_LICENSE_HEADER_END@ */ #include #include @@ -40,7 +37,6 @@ #include #include #include -#include #include "launch.h" @@ -61,9 +57,10 @@ static void find_fds(launch_data_t o, const char *key __attribute__((unused)), v fd = launch_data_get_fd(o); if (-1 == fd) break; - assert(fcntl(fd, F_SETFD, 1) != -1); + fcntl(fd, F_SETFD, 1); EV_SET(&kev, fd, EVFILT_READ, EV_ADD, 0, 0, NULL); - assert(kevent(kq, &kev, 1, NULL, 0, NULL) != -1); + if (kevent(kq, &kev, 1, NULL, 0, NULL) == -1) + syslog(LOG_DEBUG, "kevent(%d): %m", fd); break; case LAUNCH_DATA_ARRAY: for (i = 0; i < launch_data_array_get_count(o); i++) @@ -79,7 +76,7 @@ static void find_fds(launch_data_t o, const char *key __attribute__((unused)), v int main(int argc __attribute__((unused)), char *argv[]) { - struct timespec timeout = { 120, 0 }; + struct timespec timeout = { 10, 0 }; struct sockaddr_storage ss; socklen_t slen = sizeof(ss); struct kevent kev; @@ -143,11 +140,11 @@ int main(int argc __attribute__((unused)), char *argv[]) } if (w) { - assert(dup2(kev.ident, STDIN_FILENO) != -1); + dup2(kev.ident, STDIN_FILENO); if (dupstdout) - assert(dup2(kev.ident, STDOUT_FILENO) != -1); + dup2(kev.ident, STDOUT_FILENO); if (dupstderr) - assert(dup2(kev.ident, STDERR_FILENO) != -1); + dup2(kev.ident, STDERR_FILENO); execv(prog, argv + 1); syslog(LOG_ERR, "execv(): %m"); exit(EXIT_FAILURE); @@ -194,17 +191,16 @@ int main(int argc __attribute__((unused)), char *argv[]) syslog(LOG_NOTICE, "%s: SessionCreate == NULL!", prog); } } - assert(fcntl(r, F_SETFL, 0) != -1); - assert(dup2(r, STDIN_FILENO) != -1); + fcntl(r, F_SETFL, 0); + dup2(r, STDIN_FILENO); if (dupstdout) - assert(dup2(r, STDOUT_FILENO) != -1); + dup2(r, STDOUT_FILENO); if (dupstderr) - assert(dup2(r, STDERR_FILENO) != -1); - assert(close(r) != -1); + dup2(r, STDERR_FILENO); signal(SIGCHLD, SIG_DFL); execv(prog, argv + 1); syslog(LOG_ERR, "execv(): %m"); - _exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } } diff --git a/launchd/src/liblaunch.c b/launchd/src/liblaunch.c index d866ca9..ceebfef 100644 --- a/launchd/src/liblaunch.c +++ b/launchd/src/liblaunch.c @@ -1,42 +1,42 @@ /* * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ + * @APPLE_APACHE_LICENSE_HEADER_START@ * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * The 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, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and * limitations under the License. * - * @APPLE_LICENSE_HEADER_END@ + * @APPLE_APACHE_LICENSE_HEADER_END@ */ +#include #include #include #include #include #include #include +#include #include #include -#include #include #include #include -#include #include +#include #include "launch.h" #include "launch_priv.h" +#include "bootstrap_public.h" +#include "bootstrap_private.h" /* __OSBogusByteSwap__() must not really exist in the symbol namespace * in order for the following to generate an error at build time. @@ -91,28 +91,29 @@ extern void __OSBogusByteSwap__(void); struct launch_msg_header { uint64_t magic; - uint64_t fdcnt; uint64_t len; }; #define LAUNCH_MSG_HEADER_MAGIC 0xD2FEA02366B39A41ull struct _launch_data { - int type; + uint64_t type; union { struct { - launch_data_t *_array; - size_t _array_cnt; - }; - struct { - char *string; - size_t string_len; - }; - struct { - void *opaque; - size_t opaque_size; + union { + launch_data_t *_array; + char *string; + void *opaque; + int64_t __junk; + }; + union { + uint64_t _array_cnt; + uint64_t string_len; + uint64_t opaque_size; + }; }; int fd; + mach_port_t mp; int err; long long number; bool boolean; @@ -122,46 +123,40 @@ struct _launch_data { struct _launch { void *sendbuf; - int *sendfds; + int *sendfds; void *recvbuf; - int *recvfds; - size_t sendlen; - size_t sendfdcnt; - size_t recvlen; - size_t recvfdcnt; - int fd; + int *recvfds; + size_t sendlen; + size_t sendfdcnt; + size_t recvlen; + size_t recvfdcnt; + int fd; }; static void make_msg_and_cmsg(launch_data_t, void **, size_t *, int **, size_t *); static launch_data_t make_data(launch_t, size_t *, size_t *); +static launch_data_t launch_data_array_pop_first(launch_data_t where); static int _fd(int fd); +static void launch_client_init(void); +static void launch_msg_getmsgs(launch_data_t m, void *context); +static launch_data_t launch_msg_internal(launch_data_t d); +static void launch_mach_checkin_service(launch_data_t obj, const char *key, void *context); static pthread_once_t _lc_once = PTHREAD_ONCE_INIT; -void (*__log_liblaunch_bug)(const char *path, unsigned int line, const char *test) = NULL; - -static void -_log_liblaunch_bug(const char *path, unsigned int line, const char *test) -{ - if (__log_liblaunch_bug) - __log_liblaunch_bug(path, line, test); -} - -#define assumes(e) \ - (__builtin_expect(!(e), 0) ? _log_liblaunch_bug(__FILE__, __LINE__, #e), false : true) - static struct _launch_client { pthread_mutex_t mtx; launch_t l; launch_data_t async_resp; } *_lc = NULL; -static void launch_client_init(void) +void +launch_client_init(void) { struct sockaddr_un sun; char *where = getenv(LAUNCHD_SOCKET_ENV); char *_launchd_fd = getenv(LAUNCHD_TRUSTED_FD_ENV); - int r, dfd, lfd = -1, tries; + int dfd, lfd = -1; _lc = calloc(1, sizeof(struct _launch_client)); @@ -173,7 +168,7 @@ static void launch_client_init(void) if (_launchd_fd) { lfd = strtol(_launchd_fd, NULL, 10); if ((dfd = dup(lfd)) >= 0) { - assumes(close(dfd) != -1); + close(dfd); _fd(lfd); } else { lfd = -1; @@ -184,33 +179,21 @@ static void launch_client_init(void) memset(&sun, 0, sizeof(sun)); sun.sun_family = AF_UNIX; - if (where) + if (where && where[0] != '\0') { strncpy(sun.sun_path, where, sizeof(sun.sun_path)); - else - snprintf(sun.sun_path, sizeof(sun.sun_path), "%s/%u/sock", LAUNCHD_SOCK_PREFIX, getuid()); - - if (!assumes((lfd = _fd(socket(AF_UNIX, SOCK_STREAM, 0))) != -1)) + } else if (getuid() == 0) { + strncpy(sun.sun_path, LAUNCHD_SOCK_PREFIX "/sock", sizeof(sun.sun_path)); + } else { goto out_bad; - - for (tries = 0; tries < 10; tries++) { - r = connect(lfd, (struct sockaddr *)&sun, sizeof(sun)); - if (r == -1) { - if (getuid() != 0 && fork() == 0) - execl("/sbin/launchd", "/sbin/launchd", NULL); - sleep(1); - } else { - break; - } } - if (r == -1) { - assumes(close(lfd) != -1); + + if ((lfd = _fd(socket(AF_UNIX, SOCK_STREAM, 0))) == -1) + goto out_bad; + if (-1 == connect(lfd, (struct sockaddr *)&sun, sizeof(sun))) goto out_bad; - } } - if (!(_lc->l = launchd_fdopen(lfd))) { - close(lfd); + if (!(_lc->l = launchd_fdopen(lfd))) goto out_bad; - } if (!(_lc->async_resp = launch_data_alloc(LAUNCH_DATA_ARRAY))) goto out_bad; @@ -218,12 +201,15 @@ static void launch_client_init(void) out_bad: if (_lc->l) launchd_close(_lc->l); + else if (lfd != -1) + close(lfd); if (_lc) free(_lc); _lc = NULL; } -launch_data_t launch_data_alloc(launch_data_type_t t) +launch_data_t +launch_data_alloc(launch_data_type_t t) { launch_data_t d = calloc(1, sizeof(struct _launch)); @@ -242,12 +228,14 @@ launch_data_t launch_data_alloc(launch_data_type_t t) return d; } -launch_data_type_t launch_data_get_type(launch_data_t d) +launch_data_type_t +launch_data_get_type(launch_data_t d) { return d->type; } -void launch_data_free(launch_data_t d) +void +launch_data_free(launch_data_t d) { size_t i; @@ -272,46 +260,39 @@ void launch_data_free(launch_data_t d) free(d); } -size_t launch_data_dict_get_count(launch_data_t dict) +size_t +launch_data_dict_get_count(launch_data_t dict) { - if (!assumes(dict->type == LAUNCH_DATA_DICTIONARY)) - return 0; - return dict->_array_cnt / 2; } -bool launch_data_dict_insert(launch_data_t dict, launch_data_t what, const char *key) +bool +launch_data_dict_insert(launch_data_t dict, launch_data_t what, const char *key) { - launch_data_t thekey; size_t i; + launch_data_t thekey = launch_data_alloc(LAUNCH_DATA_STRING); - if (!assumes(dict->type == LAUNCH_DATA_DICTIONARY)) - return false; - - thekey = launch_data_new_string(key); + launch_data_set_string(thekey, key); for (i = 0; i < dict->_array_cnt; i += 2) { if (!strcasecmp(key, dict->_array[i]->string)) { - dict->type = LAUNCH_DATA_ARRAY; launch_data_array_set_index(dict, thekey, i); launch_data_array_set_index(dict, what, i + 1); - dict->type = LAUNCH_DATA_DICTIONARY; return true; } } - dict->type = LAUNCH_DATA_ARRAY; launch_data_array_set_index(dict, thekey, i); launch_data_array_set_index(dict, what, i + 1); - dict->type = LAUNCH_DATA_DICTIONARY; return true; } -launch_data_t launch_data_dict_lookup(launch_data_t dict, const char *key) +launch_data_t +launch_data_dict_lookup(launch_data_t dict, const char *key) { size_t i; - if (!assumes(dict->type == LAUNCH_DATA_DICTIONARY)) + if (LAUNCH_DATA_DICTIONARY != dict->type) return NULL; for (i = 0; i < dict->_array_cnt; i += 2) { @@ -322,13 +303,11 @@ launch_data_t launch_data_dict_lookup(launch_data_t dict, const char *key) return NULL; } -bool launch_data_dict_remove(launch_data_t dict, const char *key) +bool +launch_data_dict_remove(launch_data_t dict, const char *key) { size_t i; - if (!assumes(dict->type == LAUNCH_DATA_DICTIONARY)) - return false; - for (i = 0; i < dict->_array_cnt; i += 2) { if (!strcasecmp(key, dict->_array[i]->string)) break; @@ -342,22 +321,21 @@ bool launch_data_dict_remove(launch_data_t dict, const char *key) return true; } -void launch_data_dict_iterate(launch_data_t dict, void (*cb)(launch_data_t, const char *, void *), void *context) +void +launch_data_dict_iterate(launch_data_t dict, void (*cb)(launch_data_t, const char *, void *), void *context) { size_t i; - if (!assumes(dict->type == LAUNCH_DATA_DICTIONARY)) + if (LAUNCH_DATA_DICTIONARY != dict->type) return; for (i = 0; i < dict->_array_cnt; i += 2) cb(dict->_array[i + 1], dict->_array[i]->string, context); } -bool launch_data_array_set_index(launch_data_t where, launch_data_t what, size_t ind) +bool +launch_data_array_set_index(launch_data_t where, launch_data_t what, size_t ind) { - if (!assumes(where->type == LAUNCH_DATA_ARRAY)) - return false; - if ((ind + 1) >= where->_array_cnt) { where->_array = realloc(where->_array, (ind + 1) * sizeof(launch_data_t)); memset(where->_array + where->_array_cnt, 0, (ind + 1 - where->_array_cnt) * sizeof(launch_data_t)); @@ -370,22 +348,20 @@ bool launch_data_array_set_index(launch_data_t where, launch_data_t what, size_t return true; } -launch_data_t launch_data_array_get_index(launch_data_t where, size_t ind) +launch_data_t +launch_data_array_get_index(launch_data_t where, size_t ind) { - if (!assumes(where->type == LAUNCH_DATA_ARRAY)) + if (LAUNCH_DATA_ARRAY != where->type) return NULL; - if (ind < where->_array_cnt) return where->_array[ind]; return NULL; } -launch_data_t launch_data_array_pop_first(launch_data_t where) +launch_data_t +launch_data_array_pop_first(launch_data_t where) { launch_data_t r = NULL; - - if (!assumes(where->type == LAUNCH_DATA_ARRAY)) - return NULL; if (where->_array_cnt > 0) { r = where->_array[0]; @@ -395,57 +371,59 @@ launch_data_t launch_data_array_pop_first(launch_data_t where) return r; } -size_t launch_data_array_get_count(launch_data_t where) +size_t +launch_data_array_get_count(launch_data_t where) { - if (!assumes(where->type == LAUNCH_DATA_ARRAY)) + if (LAUNCH_DATA_ARRAY != where->type) return 0; return where->_array_cnt; } -bool launch_data_set_errno(launch_data_t d, int e) +bool +launch_data_set_errno(launch_data_t d, int e) { - if (!assumes(d->type == LAUNCH_DATA_ERRNO)) - return false; d->err = e; return true; } -bool launch_data_set_fd(launch_data_t d, int fd) +bool +launch_data_set_fd(launch_data_t d, int fd) { - if (!assumes(d->type == LAUNCH_DATA_FD)) - return false; d->fd = fd; return true; } -bool launch_data_set_integer(launch_data_t d, long long n) +bool +launch_data_set_machport(launch_data_t d, mach_port_t p) +{ + d->mp = p; + return true; +} + +bool +launch_data_set_integer(launch_data_t d, long long n) { - if (!assumes(d->type == LAUNCH_DATA_INTEGER)) - return false; d->number = n; return true; } -bool launch_data_set_bool(launch_data_t d, bool b) +bool +launch_data_set_bool(launch_data_t d, bool b) { - if (!assumes(d->type == LAUNCH_DATA_BOOL)) - return false; d->boolean = b; return true; } -bool launch_data_set_real(launch_data_t d, double n) +bool +launch_data_set_real(launch_data_t d, double n) { - if (!assumes(d->type == LAUNCH_DATA_REAL)) - return false; d->float_num = n; return true; } -bool launch_data_set_string(launch_data_t d, const char *s) +bool +launch_data_set_string(launch_data_t d, const char *s) { - if (!assumes(d->type == LAUNCH_DATA_STRING)) - return false; if (d->string) free(d->string); d->string = strdup(s); @@ -456,10 +434,9 @@ bool launch_data_set_string(launch_data_t d, const char *s) return false; } -bool launch_data_set_opaque(launch_data_t d, const void *o, size_t os) +bool +launch_data_set_opaque(launch_data_t d, const void *o, size_t os) { - if (!assumes(d->type == LAUNCH_DATA_OPAQUE)) - return false; d->opaque_size = os; if (d->opaque) free(d->opaque); @@ -471,68 +448,72 @@ bool launch_data_set_opaque(launch_data_t d, const void *o, size_t os) return false; } -int launch_data_get_errno(launch_data_t d) +int +launch_data_get_errno(launch_data_t d) { - if (!assumes(d->type == LAUNCH_DATA_ERRNO)) - return 0; return d->err; } -int launch_data_get_fd(launch_data_t d) +int +launch_data_get_fd(launch_data_t d) { - if (!assumes(d->type == LAUNCH_DATA_FD)) - return -1; return d->fd; } -long long launch_data_get_integer(launch_data_t d) +mach_port_t +launch_data_get_machport(launch_data_t d) +{ + return d->mp; +} + +long long +launch_data_get_integer(launch_data_t d) { - if (!assumes(d->type == LAUNCH_DATA_INTEGER)) - return 0; return d->number; } -bool launch_data_get_bool(launch_data_t d) +bool +launch_data_get_bool(launch_data_t d) { - if (!assumes(d->type == LAUNCH_DATA_BOOL)) - return false; return d->boolean; } -double launch_data_get_real(launch_data_t d) +double +launch_data_get_real(launch_data_t d) { - if (!assumes(d->type == LAUNCH_DATA_REAL)) - return NAN; return d->float_num; } -const char *launch_data_get_string(launch_data_t d) +const char * +launch_data_get_string(launch_data_t d) { - if (!assumes(d->type == LAUNCH_DATA_STRING)) + if (LAUNCH_DATA_STRING != d->type) return NULL; return d->string; } -void *launch_data_get_opaque(launch_data_t d) +void * +launch_data_get_opaque(launch_data_t d) { - if (!assumes(d->type == LAUNCH_DATA_OPAQUE)) + if (LAUNCH_DATA_OPAQUE != d->type) return NULL; return d->opaque; } -size_t launch_data_get_opaque_size(launch_data_t d) +size_t +launch_data_get_opaque_size(launch_data_t d) { - if (!assumes(d->type == LAUNCH_DATA_OPAQUE)) - return 0; return d->opaque_size; } -int launchd_getfd(launch_t l) +int +launchd_getfd(launch_t l) { return l->fd; } -launch_t launchd_fdopen(int fd) +launch_t +launchd_fdopen(int fd) { launch_t c; @@ -542,7 +523,7 @@ launch_t launchd_fdopen(int fd) c->fd = fd; - assumes(fcntl(fd, F_SETFL, O_NONBLOCK) != -1); + fcntl(fd, F_SETFL, O_NONBLOCK); if ((c->sendbuf = malloc(0)) == NULL) goto out_bad; @@ -568,7 +549,8 @@ out_bad: return NULL; } -void launchd_close(launch_t lh) +void +launchd_close(launch_t lh) { if (lh->sendbuf) free(lh->sendbuf); @@ -578,11 +560,12 @@ void launchd_close(launch_t lh) free(lh->recvbuf); if (lh->recvfds) free(lh->recvfds); - assumes(close(lh->fd) != -1); + close(lh->fd); free(lh); } -static void make_msg_and_cmsg(launch_data_t d, void **where, size_t *len, int **fd_where, size_t *fdcnt) +void +make_msg_and_cmsg(launch_data_t d, void **where, size_t *len, int **fd_where, size_t *fdcnt) { launch_data_t o_in_w; size_t i; @@ -706,6 +689,7 @@ static launch_data_t make_data(launch_t conn, size_t *data_offset, size_t *fdoff break; case LAUNCH_DATA_ERRNO: r->err = big2host(r->err); + case LAUNCH_DATA_MACHPORT: break; default: errno = EINVAL; @@ -736,7 +720,6 @@ int launchd_msg_send(launch_t lh, launch_data_t d) msglen = (lh->sendlen - msglen) + sizeof(struct launch_msg_header); lmh.len = host2big(msglen); - lmh.fdcnt = 0; lmh.magic = host2big(LAUNCH_MSG_HEADER_MAGIC); iov[0].iov_base = &lmh; @@ -753,7 +736,6 @@ int launchd_msg_send(launch_t lh, launch_data_t d) if (lh->sendfdcnt > 0) { - lmh.fdcnt = host2big((uint64_t)lh->sendfdcnt); sentctrllen = mh.msg_controllen = CMSG_SPACE(lh->sendfdcnt * sizeof(int)); cm = alloca(mh.msg_controllen); mh.msg_control = cm; @@ -765,10 +747,9 @@ int launchd_msg_send(launch_t lh, launch_data_t d) cm->cmsg_type = SCM_RIGHTS; memcpy(CMSG_DATA(cm), lh->sendfds, lh->sendfdcnt * sizeof(int)); - } - if (!assumes((r = sendmsg(lh->fd, &mh, 0)) != -1)) { + if ((r = sendmsg(lh->fd, &mh, 0)) == -1) { return -1; } else if (r == 0) { errno = ECONNRESET; @@ -803,7 +784,8 @@ int launchd_msg_send(launch_t lh, launch_data_t d) } -int launch_get_fd(void) +int +launch_get_fd(void) { pthread_once(&_lc_once, launch_client_init); @@ -815,7 +797,8 @@ int launch_get_fd(void) return _lc->l->fd; } -static void launch_msg_getmsgs(launch_data_t m, void *context) +void +launch_msg_getmsgs(launch_data_t m, void *context) { launch_data_t async_resp, *sync_resp = context; @@ -826,7 +809,44 @@ static void launch_msg_getmsgs(launch_data_t m, void *context) } } -launch_data_t launch_msg(launch_data_t d) +void +launch_mach_checkin_service(launch_data_t obj, const char *key, void *context __attribute__((unused))) +{ + kern_return_t result; + mach_port_t p; + name_t srvnm; + + strlcpy(srvnm, key, sizeof(srvnm)); + + result = bootstrap_check_in(bootstrap_port, srvnm, &p); + + if (result == BOOTSTRAP_SUCCESS) + launch_data_set_machport(obj, p); +} + +launch_data_t +launch_msg(launch_data_t d) +{ + launch_data_t mps, r = launch_msg_internal(d); + + if (launch_data_get_type(d) == LAUNCH_DATA_STRING) { + if (strcmp(launch_data_get_string(d), LAUNCH_KEY_CHECKIN) != 0) + return r; + if (r == NULL) + return r; + if (launch_data_get_type(r) != LAUNCH_DATA_DICTIONARY) + return r; + mps = launch_data_dict_lookup(r, LAUNCH_JOBKEY_MACHSERVICES); + if (mps == NULL) + return r; + launch_data_dict_iterate(mps, launch_mach_checkin_service, NULL); + } + + return r; +} + +launch_data_t +launch_msg_internal(launch_data_t d) { launch_data_t resp = NULL; @@ -841,7 +861,7 @@ launch_data_t launch_msg(launch_data_t d) if (d && launchd_msg_send(_lc->l, d) == -1) { do { - if (!assumes(errno == EAGAIN)) + if (errno != EAGAIN) goto out; } while (launchd_msg_send(_lc->l, NULL) == -1); } @@ -852,7 +872,7 @@ launch_data_t launch_msg(launch_data_t d) goto out; } if (launchd_msg_recv(_lc->l, launch_msg_getmsgs, &resp) == -1) { - if (!assumes(errno == EAGAIN)) { + if (errno != EAGAIN) { goto out; } else if (d == NULL) { errno = 0; @@ -863,7 +883,7 @@ launch_data_t launch_msg(launch_data_t d) FD_ZERO(&rfds); FD_SET(_lc->l->fd, &rfds); - assumes(select(_lc->l->fd + 1, &rfds, NULL, NULL, NULL) == 1); + select(_lc->l->fd + 1, &rfds, NULL, NULL, NULL); } } } @@ -894,22 +914,18 @@ int launchd_msg_recv(launch_t lh, void (*cb)(launch_data_t, void *), void *conte mh.msg_control = cm; mh.msg_controllen = 4096; - if (!assumes((r = recvmsg(lh->fd, &mh, 0)) != -1)) + if ((r = recvmsg(lh->fd, &mh, 0)) == -1) return -1; if (r == 0) { errno = ECONNRESET; return -1; } - if (!assumes(!(mh.msg_flags & MSG_CTRUNC))) { + if (mh.msg_flags & MSG_CTRUNC) { errno = ECONNABORTED; return -1; } lh->recvlen += r; if (mh.msg_controllen > 0) { - if (!assumes(cm->cmsg_len == mh.msg_controllen)) { - errno = ESPIPE; - return -1; - } lh->recvfds = realloc(lh->recvfds, lh->recvfdcnt * sizeof(int) + mh.msg_controllen - sizeof(struct cmsghdr)); memcpy(lh->recvfds + lh->recvfdcnt, CMSG_DATA(cm), mh.msg_controllen - sizeof(struct cmsghdr)); lh->recvfdcnt += (mh.msg_controllen - sizeof(struct cmsghdr)) / sizeof(int); @@ -928,17 +944,11 @@ int launchd_msg_recv(launch_t lh, void (*cb)(launch_data_t, void *), void *conte tmplen = big2host(lmhp->len); - if (!assumes(big2host(lmhp->magic) == LAUNCH_MSG_HEADER_MAGIC) || - !assumes(tmplen > sizeof(struct launch_msg_header))) { + if (big2host(lmhp->magic) != LAUNCH_MSG_HEADER_MAGIC || tmplen <= sizeof(struct launch_msg_header)) { errno = EBADRPC; goto out_bad; } - if (!assumes(big2host(lmhp->fdcnt) == lh->recvfdcnt)) { - errno = ERANGE; - return -1; - } - if (lh->recvlen < tmplen) { goto need_more_data; } @@ -965,9 +975,6 @@ int launchd_msg_recv(launch_t lh, void (*cb)(launch_data_t, void *), void *conte free(lh->recvfds); lh->recvfds = malloc(0); } - - if (lh->recvlen == 0) - assumes(lh->recvfdcnt == 0); } return r; @@ -1049,7 +1056,7 @@ bool launchd_batch_query(void) static int _fd(int fd) { if (fd >= 0) - assumes(fcntl(fd, F_SETFD, 1) != -1); + fcntl(fd, F_SETFD, 1); return fd; } @@ -1073,6 +1080,16 @@ launch_data_t launch_data_new_fd(int fd) return r; } +launch_data_t launch_data_new_machport(mach_port_t p) +{ + launch_data_t r = launch_data_alloc(LAUNCH_DATA_MACHPORT); + + if (r) + launch_data_set_machport(r, p); + + return r; +} + launch_data_t launch_data_new_integer(long long n) { launch_data_t r = launch_data_alloc(LAUNCH_DATA_INTEGER); @@ -1132,3 +1149,88 @@ launch_data_t launch_data_new_opaque(const void *o, size_t os) return r; } + +static pid_t +fexecv_as_user(const char *login, uid_t u, gid_t g, char *const argv[]) +{ + int i, dtsz; + pid_t p; + + if ((p = fork()) != 0) + return p; + + chdir("/"); + + seteuid(0); + setegid(0); + initgroups(login, g); + setgid(g); + setuid(u); + + dtsz = getdtablesize(); + + for (i = STDERR_FILENO + 1; i < dtsz; i++) + close(i); + + execv(argv[0], argv); + _exit(EXIT_FAILURE); +} + +pid_t +create_and_switch_to_per_session_launchd(const char *login, int flags, ...) +{ + static char *const ldargv[] = { "/sbin/launchd", "-S", "Aqua", NULL }; + char *largv[] = { "/bin/launchctl", "load", "-S", "Aqua", "-D", "all", "/etc/mach_init_per_user.d", NULL }; + mach_port_t bezel_ui_server; + struct passwd *pwe; + struct stat sb; + int wstatus; + name_t sp; + pid_t p, ldp; + uid_t u; + gid_t g; + + if ((pwe = getpwnam(login)) == NULL) + return -1; + + u = pwe->pw_uid; + g = pwe->pw_gid; + + if ((ldp = fexecv_as_user(login, u, g, ldargv)) == -1) + return -1; + + while (bootstrap_getsocket(bootstrap_port, sp) != BOOTSTRAP_SUCCESS) + usleep(20000); + + setenv(LAUNCHD_SOCKET_ENV, sp, 1); + + if (flags & LOAD_ONLY_SAFEMODE_LAUNCHAGENTS) + largv[5] = "system"; + + if ((p = fexecv_as_user(login, u, g, largv)) == -1) + return -1; + + if (waitpid(p, &wstatus, 0) != p) + return -1; + + if (!(WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 0)) + return -1; + +#define BEZEL_UI_PATH "/System/Library/LoginPlugins/BezelServices.loginPlugin/Contents/Resources/BezelUI/BezelUIServer" +#define BEZEL_UI_PLIST "/System/Library/LaunchAgents/com.apple.BezelUIServer.plist" +#define BEZEL_UI_SERVICE "BezelUI" + + if (!(stat(BEZEL_UI_PLIST, &sb) == 0 && S_ISREG(sb.st_mode))) { + if (bootstrap_create_server(bootstrap_port, BEZEL_UI_PATH, u, true, &bezel_ui_server) == BOOTSTRAP_SUCCESS) { + mach_port_t srv; + + if (bootstrap_create_service(bezel_ui_server, BEZEL_UI_SERVICE, &srv) == BOOTSTRAP_SUCCESS) { + mach_port_deallocate(mach_task_self(), srv); + } + + mach_port_deallocate(mach_task_self(), bezel_ui_server); + } + } + + return ldp; +} diff --git a/launchd/src/lists.c b/launchd/src/lists.c deleted file mode 100644 index 01d3ef9..0000000 --- a/launchd/src/lists.c +++ /dev/null @@ -1,485 +0,0 @@ -/* - * Copyright (c) 1999-2004 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights - * Reserved. This file contains Original Code and/or Modifications of - * Original Code as defined in and that are subject to the Apple Public - * Source License Version 1.0 (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. - * - * The 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@ - */ -/* - * bootstrap -- fundamental service initiator and port server - * Mike DeMoney, NeXT, Inc. - * Copyright, 1990. All rights reserved. - * - * lists.c -- implementation of list handling routines - */ - -#include -#include - -#include -#include -#include - -#include - -#include "bootstrap_internal.h" -#include "lists.h" - -/* - * Exports - */ -bootstrap_info_t bootstraps; /* head of list of all bootstrap ports */ -server_t servers; /* head of list of all servers */ -service_t services; /* head of list of all services */ -unsigned nservices; /* number of services in list */ - -#ifndef ASSERT -#define ASSERT(p) -#endif - -/* - * Private macros - */ -#define NEW(type, num) ((type *)ckmalloc(sizeof(type) * num)) -#define STREQ(a, b) (strcmp(a, b) == 0) -#define NELEM(x) (sizeof(x)/sizeof((x)[0])) -#define LAST_ELEMENT(x) ((x)[NELEM(x)-1]) - -void -init_lists(void) -{ - bootstraps.ref_count = 2; /* make sure we never deallocate this one */ - bootstraps.next = bootstraps.prev = &bootstraps; - bootstraps.parent = &bootstraps; - servers.next = servers.prev = &servers; - services.next = services.prev = &services; - nservices = 0; -} - -server_t * -new_server( - bootstrap_info_t *bootstrap, - const char *cmd, - uid_t uid, - servertype_t servertype, - auditinfo_t auinfo) -{ - server_t *serverp; - - syslog(LOG_DEBUG, "adding new server \"%s\" with uid %d", cmd, uid); - serverp = NEW(server_t, 1); - if (serverp != NULL) { - /* Doubly linked list */ - servers.prev->next = serverp; - serverp->prev = servers.prev; - serverp->next = &servers; - servers.prev = serverp; - - bootstrap->ref_count++; - serverp->bootstrap = bootstrap; - - serverp->pid = NO_PID; - serverp->task_port = MACH_PORT_NULL; - - serverp->uid = uid; - serverp->auinfo = auinfo; - - serverp->port = MACH_PORT_NULL; - serverp->servertype = servertype; - serverp->activity = 0; - serverp->active_services = 0; - strncpy(serverp->cmd, cmd, sizeof serverp->cmd); - LAST_ELEMENT(serverp->cmd) = '\0'; - } - return serverp; -} - -service_t * -new_service( - bootstrap_info_t *bootstrap, - const char *name, - mach_port_t serviceport, - boolean_t isActive, - servicetype_t servicetype, - server_t *serverp) -{ - service_t *servicep; - - servicep = NEW(service_t, 1); - if (servicep != NULL) { - /* Doubly linked list */ - services.prev->next = servicep; - servicep->prev = services.prev; - servicep->next = &services; - services.prev = servicep; - - nservices += 1; - - strncpy(servicep->name, name, sizeof servicep->name); - LAST_ELEMENT(servicep->name) = '\0'; - servicep->servicetype = servicetype; - servicep->bootstrap = bootstrap; - servicep->port = serviceport; - servicep->server = serverp; - servicep->isActive = isActive; - } - return servicep; -} - -bootstrap_info_t * -new_bootstrap( - bootstrap_info_t *parent, - mach_port_t bootstrapport, - mach_port_t requestorport) -{ - bootstrap_info_t *bootstrap; - - bootstrap = NEW(bootstrap_info_t, 1); - if (bootstrap != NULL) { - /* Doubly linked list */ - bootstraps.prev->next = bootstrap; - bootstrap->prev = bootstraps.prev; - bootstrap->next = &bootstraps; - bootstraps.prev = bootstrap; - - bootstrap->bootstrap_port = bootstrapport; - bootstrap->requestor_port = requestorport; - - bootstrap->ref_count = 1; - bootstrap->parent = parent; - parent->ref_count++; - } - return bootstrap; -} - -bootstrap_info_t * -lookup_bootstrap_by_port(mach_port_t port) -{ - bootstrap_info_t *bootstrap; - bootstrap_info_t *first; - server_t *serverp; - - bootstrap = first = FIRST(bootstraps); - do { - if (bootstrap->bootstrap_port == port) - return bootstrap; - bootstrap = NEXT(bootstrap); - } while (bootstrap != first); - - for ( serverp = FIRST(servers) - ; !IS_END(serverp, servers) - ; serverp = NEXT(serverp)) - { - if (port == serverp->port) - return serverp->bootstrap; - } - return NULL; -} - -bootstrap_info_t * -lookup_bootstrap_by_req_port(mach_port_t port) -{ - bootstrap_info_t *bootstrap; - - for ( bootstrap = FIRST(bootstraps) - ; !IS_END(bootstrap, bootstraps) - ; bootstrap = NEXT(bootstrap)) - { - if (bootstrap->requestor_port == port) - return bootstrap; - } - - return NULL; -} - -service_t * -lookup_service_by_name(bootstrap_info_t *bootstrap, name_t name) -{ - service_t *servicep; - - if (bootstrap) - do { - for ( servicep = FIRST(services) - ; !IS_END(servicep, services) - ; servicep = NEXT(servicep)) - { - if (!STREQ(name, servicep->name)) - continue; - if (bootstrap && servicep->bootstrap != bootstrap) - continue; - return servicep; - } - } while (bootstrap != &bootstraps && - (bootstrap = bootstrap->parent)); - return NULL; -} - -void -unlink_service(service_t *servicep) -{ - ASSERT(servicep->prev->next == servicep); - ASSERT(servicep->next->prev == servicep); - servicep->prev->next = servicep->next; - servicep->next->prev = servicep->prev; - servicep->prev = servicep->next = servicep; // idempotent -} - -void -delete_service(service_t *servicep) -{ - unlink_service(servicep); - switch (servicep->servicetype) { - case REGISTERED: - syslog(LOG_INFO, "Registered service %s deleted", servicep->name); - mach_port_deallocate(mach_task_self(), servicep->port); - break; - case DECLARED: - syslog(LOG_INFO, "Declared service %s now unavailable", servicep->name); - mach_port_deallocate(mach_task_self(), servicep->port); - mach_port_mod_refs(mach_task_self(), servicep->port, - MACH_PORT_RIGHT_RECEIVE, -1); - break; - default: - syslog(LOG_ERR, "unknown service type %d", servicep->servicetype); - break; - } - free(servicep); - nservices -= 1; -} - -void -delete_bootstrap_services(bootstrap_info_t *bootstrap) -{ - server_t *serverp; - service_t *servicep; - service_t *next; - - for ( servicep = FIRST(services) - ; !IS_END(servicep, services) - ; servicep = next) - { - next = NEXT(servicep); - if (bootstrap != servicep->bootstrap) - continue; - - serverp = servicep->server; - - if (servicep->isActive && serverp) - serverp->active_services--; - - delete_service(servicep); - - if (!serverp) - continue; - if (!active_server(serverp)) - delete_server(serverp); - } -} - -service_t * -lookup_service_by_port(mach_port_t port) -{ - service_t *servicep; - - for ( servicep = FIRST(services) - ; !IS_END(servicep, services) - ; servicep = NEXT(servicep)) - { - if (port == servicep->port) - return servicep; - } - return NULL; -} - -service_t * -lookup_service_by_server(server_t *serverp) -{ - service_t *servicep; - - for ( servicep = FIRST(services) - ; !IS_END(servicep, services) - ; servicep = NEXT(servicep)) - { - if (serverp == servicep->server) - return servicep; - } - return NULL; -} - -server_t * -lookup_server_by_task_port(mach_port_t port) -{ - server_t *serverp; - - for ( serverp = FIRST(servers) - ; !IS_END(serverp, servers) - ; serverp = NEXT(serverp)) - { - if (port == serverp->task_port) - return serverp; - } - return NULL; -} - -server_t * -lookup_server_by_port(mach_port_t port) -{ - server_t *serverp; - - for ( serverp = FIRST(servers) - ; !IS_END(serverp, servers) - ; serverp = NEXT(serverp)) - { - if (port == serverp->port) - return serverp; - } - return NULL; -} - -void -delete_server(server_t *serverp) -{ - service_t *servicep; - service_t *next; - - syslog(LOG_INFO, "Deleting server %s", serverp->cmd); - ASSERT(serverp->prev->next == serverp); - ASSERT(serverp->next->prev == serverp); - serverp->prev->next = serverp->next; - serverp->next->prev = serverp->prev; - - for ( servicep = FIRST(services) - ; !IS_END(servicep, services) - ; servicep = next) - { - next = NEXT(servicep); - if (serverp == servicep->server) - delete_service(servicep); - } - - deallocate_bootstrap(serverp->bootstrap); - - if (serverp->port) - mach_port_mod_refs(mach_task_self(), serverp->port, - MACH_PORT_RIGHT_RECEIVE, -1); - - free(serverp); -} - -void -deactivate_bootstrap(bootstrap_info_t *bootstrap) -{ - bootstrap_info_t *deactivating_bootstraps; - bootstrap_info_t *query_bootstrap; - bootstrap_info_t *next_limit; - bootstrap_info_t *limit; - - /* - * we need to recursively deactivate the whole subset tree below - * this point. But we don't want to do real recursion because - * we don't have a limit on the depth. So, build up a chain of - * active bootstraps anywhere underneath this one. - */ - deactivating_bootstraps = bootstrap; - bootstrap->deactivate = NULL; - for (next_limit = deactivating_bootstraps, limit = NULL - ; deactivating_bootstraps != limit - ; limit = next_limit, next_limit = deactivating_bootstraps) - { - for (bootstrap = deactivating_bootstraps - ; bootstrap != limit - ; bootstrap = bootstrap->deactivate) - { - for ( query_bootstrap = FIRST(bootstraps) - ; !IS_END(query_bootstrap, bootstraps) - ; query_bootstrap = NEXT(query_bootstrap)) - { - if (query_bootstrap->parent == bootstrap && - query_bootstrap->requestor_port != MACH_PORT_NULL) { - mach_port_deallocate( - mach_task_self(), - query_bootstrap->requestor_port); - query_bootstrap->requestor_port = MACH_PORT_NULL; - query_bootstrap->deactivate = deactivating_bootstraps; - deactivating_bootstraps = query_bootstrap; - } - } - } - } - - /* - * The list is ordered with the furthest away progeny being - * at the front, and concluding with the one we started with. - * This allows us to safely deactivate and remove the reference - * each holds on their parent without fear of the chain getting - * corrupted (because each active parent holds a reference on - * itself and that doesn't get removed until we reach its spot - * in the list). - */ - do { - bootstrap = deactivating_bootstraps; - deactivating_bootstraps = bootstrap->deactivate; - - syslog(LOG_INFO, "deactivating bootstrap %x", bootstrap->bootstrap_port); - - delete_bootstrap_services(bootstrap); - - mach_port_deallocate(mach_task_self(), bootstrap->bootstrap_port); - - { - mach_port_t previous; - mach_port_request_notification( - mach_task_self(), - bootstrap->bootstrap_port, - MACH_NOTIFY_NO_SENDERS, - 1, - bootstrap->bootstrap_port, - MACH_MSG_TYPE_MAKE_SEND_ONCE, - &previous); - } - } while (deactivating_bootstraps != NULL); -} - -void -deallocate_bootstrap(bootstrap_info_t *bootstrap) -{ - ASSERT(bootstrap->prev->next == bootstrap); - ASSERT(bootstrap->next->prev == bootstrap); - if (--bootstrap->ref_count > 0) - return; - - bootstrap->prev->next = bootstrap->next; - bootstrap->next->prev = bootstrap->prev; - deallocate_bootstrap(bootstrap->parent); - free(bootstrap); -} - -void * -ckmalloc(unsigned nbytes) -{ - void *cp; - - if ((cp = malloc(nbytes)) == NULL) { - syslog(LOG_EMERG, "malloc(): %m"); - exit(EXIT_FAILURE); - } - return cp; -} - - diff --git a/launchd/src/lists.h b/launchd/src/lists.h deleted file mode 100644 index 1d65fd9..0000000 --- a/launchd/src/lists.h +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright (c) 1999-2004 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights - * Reserved. This file contains Original Code and/or Modifications of - * Original Code as defined in and that are subject to the Apple Public - * Source License Version 1.0 (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. - * - * The 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@ - */ -/* - * bootstrap -- fundamental service initiator and port server - * Mike DeMoney, NeXT, Inc. - * Copyright, 1990. All rights reserved. - * - * lists.h -- interface to list routines - */ - -#include -#include -#include -#include - -#include - -#ifndef NULL -#define NULL ((void *)0) -#endif NULL - -typedef struct bootstrap bootstrap_info_t; -typedef struct service service_t; -typedef struct server server_t; - -/* Bootstrap info */ -struct bootstrap { - bootstrap_info_t *next; /* list of all bootstraps */ - bootstrap_info_t *prev; - bootstrap_info_t *parent; - bootstrap_info_t *deactivate; /* list being deactivated */ - mach_port_name_t bootstrap_port; - mach_port_name_t requestor_port; - unsigned int ref_count; -}; - -/* Service types */ -typedef enum { - DECLARED, /* Declared in config file */ - REGISTERED /* Registered dynamically */ -} servicetype_t; - -struct service { - service_t *next; /* list of all services */ - service_t *prev; - name_t name; /* service name */ - mach_port_name_t port; /* service port, - may have all rights if inactive */ - bootstrap_info_t *bootstrap; /* bootstrap port(s) used at this - * level. */ - boolean_t isActive; /* server is running */ - servicetype_t servicetype; /* Declared, Registered, or Machport */ - server_t *server; /* server, declared services only */ -}; - -/* Server types */ -typedef enum { - SERVER, /* Launchable server */ - RESTARTABLE, /* Restartable server */ - DEMAND, /* Restartable server - on demand */ - MACHINIT, /* mach_init doesn't get launched. */ -} servertype_t; - -#define NULL_SERVER NULL -#define ACTIVE TRUE - -struct server { - server_t *next; /* list of all servers */ - server_t *prev; - servertype_t servertype; - cmd_t cmd; /* server command to exec */ - uid_t uid; /* uid to exec server with */ - auditinfo_t auinfo; /* server's audit information */ - mach_port_t port; /* server's priv bootstrap port */ - mach_port_t task_port; /* server's task port */ - pid_t pid; /* server's pid */ - int activity; /* count of checkins/registers this instance */ - int active_services;/* count of active services */ - bootstrap_info_t *bootstrap; /* bootstrap context */ -}; - -#define NO_PID (-1) - -extern void init_lists(void); - -extern server_t *new_server( - bootstrap_info_t *bootstrap, - const char *cmd, - uid_t uid, - servertype_t servertype, - auditinfo_t auinfo); - -extern service_t *new_service( - bootstrap_info_t *bootstrap, - const char *name, - mach_port_t serviceport, - boolean_t isActive, - servicetype_t servicetype, - server_t *serverp); - -extern bootstrap_info_t *new_bootstrap( - bootstrap_info_t *parent, - mach_port_name_t bootstrapport, - mach_port_name_t requestorport); - -extern server_t *lookup_server_by_port(mach_port_t port); -extern server_t *lookup_server_by_task_port(mach_port_t port); -extern void setup_server(server_t *serverp); -extern void delete_server(server_t *serverp); -extern boolean_t active_server(server_t *serverp); -extern boolean_t useless_server(server_t *serverp); - -extern void delete_service(service_t *servicep); -extern service_t *lookup_service_by_name(bootstrap_info_t *bootstrap, name_t name); -extern service_t *lookup_service_by_port(mach_port_t port); -extern service_t *lookup_service_by_server(server_t *serverp); - -extern bootstrap_info_t *lookup_bootstrap_by_port(mach_port_t port); -extern bootstrap_info_t *lookup_bootstrap_by_req_port(mach_port_t port); -extern void deactivate_bootstrap(bootstrap_info_t *bootstrap); -extern void deallocate_bootstrap(bootstrap_info_t *bootstrap); -extern boolean_t active_bootstrap(bootstrap_info_t *bootstrap); - -extern void *ckmalloc(unsigned nbytes); - -extern bootstrap_info_t bootstraps; /* head of list of bootstrap ports */ -extern server_t servers; /* head of list of all servers */ -extern service_t services; /* head of list of all services */ -extern unsigned nservices; /* number of services in list */ - -#define FIRST(q) ((q).next) -#define NEXT(qe) ((qe)->next) -#define PREV(qe) ((qe)->prev) -#define IS_END(qe, q) ((qe) == &(q)) diff --git a/launchd/src/mpm_reply.defs b/launchd/src/mpm_reply.defs new file mode 100644 index 0000000..3389098 --- /dev/null +++ b/launchd/src/mpm_reply.defs @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_APACHE_LICENSE_HEADER_START@ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @APPLE_APACHE_LICENSE_HEADER_END@ + */ + +subsystem mpm_reply 500; + +#include +#include +import "bootstrap_public.h"; +import "bootstrap_private.h"; + +type pid_t = integer_t; + +skip; /* create_server */ + +skip; /* unprivileged */ + +skip; /* check_in */ + +skip; /* register */ + +skip; /* look_up */ + +skip; + +skip; /* parent */ + +skip; + +skip; /* info */ + +skip; /* subset */ + +skip; /* create_service */ + +skip; /* transfer_subset */ + +skip; /* getsocket */ + +skip; /* spawn */ + +simpleroutine mpm_wait_reply( + __r_port : mach_port_move_send_once_t; + __result : kern_return_t, RetCode; + __wait : integer_t); diff --git a/launchd/src/rc b/launchd/src/rc deleted file mode 100644 index e9a319f..0000000 --- a/launchd/src/rc +++ /dev/null @@ -1,302 +0,0 @@ -#!/bin/sh -# Copyright 1997-2004 Apple Computer, Inc. - -. /etc/rc.common - -export -n SafeBoot -export -n VerboseFlag -export -n FsckSlash -export -n NetBoot - -if [ -d /System/Installation -a -f /etc/rc.cdrom ]; then - /etc/rc.cdrom multiuser - # We shouldn't get here; CDIS should reboot the machine when done - echo "CD-ROM boot procedure complete" - halt -fi - -if [ "${NetBoot}" = "-N" ] ; then - echo "Initializing NetBoot" - if ! sh /etc/rc.netboot start ; then - echo NetBoot initialization failed, shut down in 10 seconds... - sleep 10 - halt - fi -fi - -if [ "${FsckSlash}" = "-F" ]; then - # Benignly clean up ("preen") any dirty filesystems. - # fsck -p will skip disks which were properly unmounted during - # a normal shutdown. - echo "Checking disk" - if [ "${SafeBoot}" = "-x" ]; then - fsck -fy || halt - else - fsck -p || fsck -fy || halt - fi -fi - -echo "Mounting local filesystems" -mount -uw / -[ -f /etc/fstab ] && mount -vat nonfs - -BootCacheControl=/System/Library/Extensions/BootCache.kext/Contents/Resources/BootCacheControl -if [ ! -f $BootCacheControl ]; then - BootCacheControl=/usr/sbin/BootCacheControl -fi -if [ "${SafeBoot}" != "-x" -a -x "${BootCacheControl}" ]; then - ${BootCacheControl} start -fi - -if [ -f /etc/rc.server ]; then - sh /etc/rc.server -fi - -if [ -f /etc/sysctl.conf ]; then - awk '{ if (!index($1, "#") && index($1, "=")) print $1 }' < /etc/sysctl.conf | while read - do - sysctl -w ${REPLY} - done -fi - -sysctl -w kern.sysv.shmmax=4194304 kern.sysv.shmmin=1 kern.sysv.shmmni=32 kern.sysv.shmseg=8 kern.sysv.shmall=1024 - -if [ -f /var/account/acct ]; then - accton /var/account/acct -fi - -echo "Resetting files and devices" - -RMRF_ITEMS="/mach.sym /var/tmp/folders.*" -## -# Attempt to recover the passwd file, if needed. This procedure is -# primarily historical and makes sense only when the passwd file is edited -# using the vipw command. -## -if [ -s /etc/ptmp ]; then - if [ -s /etc/passwd ]; then - echo -n "Passwd file conflict with ptmp: " - ls -l /etc/passwd /etc/ptmp - echo "Moving ptmp to ptmp.save" - mv -f /etc/ptmp /etc/ptmp.save - else - echo "Passwd file recovered from ptmp" - mv /etc/ptmp /etc/passwd - fi -elif [ -r /etc/ptmp ]; then - echo "Removing passwd lock file" - RMRF_ITEMS="$RMRF_ITEMS /etc/ptmp" -fi - -## -# If the shutdown command was used to shut the system down, the file -# /etc/nologin may have been created to prevent users from logging in. -# Remove it so that logins are enabled when the system comes up. -## -RMRF_ITEMS="$RMRF_ITEMS /etc/nologin" - -# Clean out /private/tmp. -if [ -d /private/tmp ]; then - # blow away any _tmp_ in case it exists as well - if [ -e /private/_tmp_ ]; then - chflags -R -P 0 /private/_tmp_ - rm -rf /private/_tmp_ - fi - mv /private/tmp /private/_tmp_ - chflags -R -P 0 /private/_tmp_ - RMRF_ITEMS="$RMRF_ITEMS /private/_tmp_" -fi -mkdir -p -m 01777 /private/var/tmp /private/tmp - -# Move /var/run out of the way -if [ -d /var/run ]; then - # blow away any _run_ in case it exists as well - if [ -e /var/_run_ ]; then - chflags -R -P 0 /var/_run_ - rm -rf /var/_run_ - fi - mv /var/run /var/_run_ -fi - -# Make new /var/run -mkdir -m 775 /var/run -mkdir -m 775 /var/run/StartupItems /var/run/davlocks -mkdir -m 755 /var/run/proxy -chown root:daemon /var/run /var/run/StartupItems -chown www:www /var/run/proxy /var/run/davlocks - -# Move sudo back to /var/run, touch the contents of /var/run/sudo/* back to the epoch -if [ -d /var/_run_/sudo ]; then - mv /var/_run_/sudo /var/run/sudo - touch -t 198501010000 /var/run/sudo/* -fi - -# Clear utmp (who is logged on). -touch /var/run/utmp /var/run/utmpx - -# purge the _run_ directory if it exists -if [ -d /var/_run_ ]; then - chflags -R -P 0 /var/_run_ - RMRF_ITEMS="$RMRF_ITEMS /var/_run_" -fi - -# Clear /var/spool/lock -if [ -d /var/spool/lock ]; then - find /var/spool/lock -not -type d -print0 | xargs -0 rm -f -fi - - -# if "/Desktop Folder" exists and has contents, make sure there is a -# "/Desktop (Mac OS 9)" symlink to it -# if "/Desktop Folder" does not exist, exists but has no contents, or exists -# and has only a single file, ".DS_Store" then there should be no -# "/Desktop (Mac OS 9)" symlink -# if there is some other file or directory with the name "/Desktop (Mac OS 9)" -# then just exit -needlink=0 -if [ -d "/Desktop Folder" ]; then - needlink=$(ls -a1 "/Desktop Folder" | wc -l) - if [ "${needlink}" -eq 3 ]; then - if [ -f "/Desktop Folder/.DS_Store" ]; then - needlink=0 - fi - fi -fi - -if [ "${needlink}" -lt 3 ]; then - if [ -h "/Desktop (Mac OS 9)" ]; then - rm -f "/Desktop (Mac OS 9)" - fi -else - if ! [ -e "/Desktop (Mac OS 9)" ]; then - ln -s "/Desktop Folder" "/Desktop (Mac OS 9)" - fi -fi - -echo "Starting virtual memory" - -swapdir=/private/var/vm -if [ "${NetBoot}" = "-N" ]; then - sh /etc/rc.netboot setup_vm ${swapdir} -fi - -if [ ! -d ${swapdir} ]; then - echo "Creating default swap directory" - mkdir -p -m 755 ${swapdir} - chown root:wheel ${swapdir} -else - RMRF_ITEMS="${RMRF_ITEMS} ${swapdir}/swap*" -fi - -echo Removing $RMRF_ITEMS -rm -rf $RMRF_ITEMS - -if [ ${ENCRYPTSWAP:=-NO-} = "-YES-" ]; then - encryptswap="-E" -else - encryptswap="" -fi -/sbin/dynamic_pager ${encryptswap} -F ${swapdir}/swapfile - -appprofiledir=/private/var/vm/app_profile - -if [ ! -d ${appprofiledir} ]; then - if [ -f ${appprofiledir} ]; then - mv -f ${appprofiledir} "${appprofiledir}_" - fi - mkdir -p -m 711 ${appprofiledir} - chown root:wheel ${appprofiledir} -fi - -# Create mach symbol file -sysctl -n kern.symfile -if [ -f /mach.sym ]; then - ln -sf /mach.sym /mach -else - ln -sf /mach_kernel /mach -fi - -if [ "${SafeBoot}" = "-x" ]; then - echo "Configuring kernel extensions for safe boot" - touch /private/tmp/.SafeBoot - kextd -x -else - echo "Configuring kernel extensions" - kextd -fi - -if [ -f /etc/rc.installer_cleanup ]; then - /etc/rc.installer_cleanup multiuser -fi - -if [ "${SafeBoot}" != "-x" -a -x "${BootCacheControl}" ]; then - ${BootCacheControl} tag -fi - -# Create local NetInfo database if it doesn't exist -if [ ! -d /var/db/netinfo/local.nidb ]; then - echo "Creating local NetInfo database" - mkdir -p /var/db/netinfo - /usr/libexec/create_nidb - rm -f /var/db/.AppleSetupDone -fi - -if [ -f /etc/security/rc.audit ]; then - . /etc/security/rc.audit -fi - -if [ -f /Library/Preferences/com.apple.sharing.firewall.plist ]; then - /usr/libexec/FirewallTool -fi - -# Load [ideally on demand] daemons -if [ "${SafeBoot}" = "-x" ]; then - launchctl load /System/Library/LaunchDaemons /etc/mach_init.d -else - launchctl load /Library/LaunchDaemons /System/Library/LaunchDaemons /etc/mach_init.d - SystemStarter ${VerboseFlag} -fi - -/usr/sbin/update - -if [ "${NetBoot}" = "-N" ]; then - sh /etc/rc.netboot setup_computername -fi - -# Set language from CDIS.custom - assumes this is parse-able by sh -if [ -f /var/log/CDIS.custom ]; then - . /var/log/CDIS.custom -fi - -# Start the Language Chooser. This code should be able to go away as far as I -# know once loginwindow becomes more dynamic. -STARTPBS="/System/Library/CoreServices/pbs" # apps stall w/o -BUDDYCOOKIE="/var/db/.AppleSetupDone" -LCACOOKIE="/var/db/.RunLanguageChooserToo" -STARTLCA="/System/Library/CoreServices/Language Chooser.app/Contents/MacOS/Language Chooser" - -if [ -x "$STARTLCA" -a -x "$STARTPBS" -a ! -f "$BUDDYCOOKIE" -a -f "$LCACOOKIE" ]; then - "$STARTPBS" & - pbspid=$! - "$STARTLCA" - kill $pbspid # XSelect does this independently -fi - -if [ ! -s "/var/log/CDIS.custom" ]; then - echo "Language Chooser seems to have failed; defaulting to English" - echo "LANGUAGE=English" > "/var/log/CDIS.custom" -fi - -export LANGUAGE - -if [ -f /etc/rc.local ]; then - sh /etc/rc.local -fi - -touch /var/run/.systemStarterRunning - -if [ "${VerboseFlag}" != "-v" ] ; then - /usr/libexec/WaitingForLoginWindow -fi - -exit 0 diff --git a/launchd/src/rc.8 b/launchd/src/rc.8 index c02fb0b..c577995 100644 --- a/launchd/src/rc.8 +++ b/launchd/src/rc.8 @@ -1,108 +1,29 @@ -.\" $NetBSD: rc.8,v 1.3 1994/11/30 19:36:26 jtc Exp $ -.\" -.\" Copyright (c) 1980, 1991, 1993 -.\" The Regents of the University of California. All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software developed by the University of -.\" California, Berkeley and its contributors. -.\" 4. Neither the name of the University nor the names of its contributors -.\" may be used to endorse or promote products derived from this software -.\" without specific prior written permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -.\" SUCH DAMAGE. -.\" -.\" @(#)rc.8 8.2 (Berkeley) 12/11/93 -.\" -.Dd December 11, 1993 +.Dd May 31, 2006 .Dt RC 8 -.Os BSD 4 +.Os Darwin 9 .Sh NAME .Nm rc -.Nd command script for auto\-reboot and daemons +.Nd command script for boot .Sh SYNOPSIS .Nm rc .Nm rc.local .Sh DESCRIPTION -.Nm Rc -is the command script which controls the automatic reboot and -.Nm rc.local -is the script holding commands which are pertinent only -to a specific site. -.Pp -When an automatic reboot is in progress, -.Nm rc -is invoked with the argument -.Em autoboot . -The first portion of +Prior to Mac OS X 10.5, the .Nm rc -runs an -.Xr fsck 8 -with option -.Fl p -to ``preen'' all the disks of minor inconsistencies resulting -from the last system shutdown and to check for serious inconsistencies -caused by hardware or software failure. -If this auto-check and repair succeeds, then the second part of -.Nm rc -is run. -.Pp -The second part of -.Nm rc , -which is run after an auto-reboot succeeds and also if -.Nm rc -is invoked when a single user shell terminates (see -.Xr init 8 ) , -starts all the daemons on the system, preserves editor files -and clears the scratch directory -.Pa /tmp . -.Pp -.Nm Rc.local -is executed immediately before any other commands after a successful -.Xr fsck . -Normally, the first commands placed in the +script +was used to bootstrap the OS. As of Leopard, the system is self-bootstrapped via +.Xr launchd 8 +which uses the +.Xr launchctl 1 +bootstrap subcommand to read in launchd jobs from the standard locations. +For compatibility reasons, the .Nm rc.local -file define the machine's name, using -.Xr hostname 1 , -and save any possible core image that might have been -generated as a result of a system crash, with -.Xr savecore 8 . -The latter command is included in the -.Nm rc.local -file because the directory in which core dumps are saved -is usually site specific. +script still continues to work. .Pp -Following tradition, the startup files -.Nm rc -and +The startup file .Nm rc.local reside in .Pa /etc . .Sh SEE ALSO -.Xr init 8 , -.Xr reboot 8 , -.Xr savecore 8 -.Sh HISTORY -The -.Nm -command appeared in -.Bx 4.0 . +.Xr launchd 8 , +.Xr launchctl 1 diff --git a/launchd/src/rc.common b/launchd/src/rc.common index 677842e..a7d1b8d 100644 --- a/launchd/src/rc.common +++ b/launchd/src/rc.common @@ -48,6 +48,8 @@ CheckForNetwork() fi } +alias ConsoleMessage=echo + ## # Process management ## diff --git a/launchd/src/rc.netboot b/launchd/src/rc.netboot index 2488976..6eab9ab 100644 --- a/launchd/src/rc.netboot +++ b/launchd/src/rc.netboot @@ -158,28 +158,24 @@ do_start() } -do_computername() +do_init() { - machine_name=$(ipconfig netbootoption machine_name 2>&1) - if [ $? -ne 0 ]; then - echo "no machine name option available" - else - echo "Setting ComputerName to ${machine_name}" - scutil --set ComputerName "${machine_name}" - fi -} + # attach the shadow file to the root disk image + do_start -do_vm() -{ - swapdir=${1:-/private/var/vm} + # make sure the root filesystem is clean + fsck -p || fsck -fy || Failed "Could not clean root filesystem" - mounted_from=$(mount | sed -n 's:\(.*\) on .*/var/netboot.*:\1:p') + # make it writable + mount -uw / + # adjust /private/var/vm to point to the writable area (if not diskless) + swapdir=/private/var/vm + mounted_from=$(mount | sed -n 's:\(.*\) on .*/var/netboot.*:\1:p') case "${mounted_from}" in /dev/*) netboot_dir="${NETBOOT_MOUNT}/.com.apple.NetBootX" if [ -d "${netboot_dir}" ]; then - rm -rf "${netboot_dir}/app_profile" rm -rf "${swapdir}" ln -s "${netboot_dir}" "${swapdir}" fi @@ -187,8 +183,18 @@ do_vm() *) ;; esac + + # set the ComputerName based on what the NetBoot server told us it was + machine_name=$(ipconfig netbootoption machine_name 2>&1) + if [ $? -ne 0 ]; then + echo "no machine name option available" + else + echo "Setting ComputerName to ${machine_name}" + scutil --set ComputerName "${machine_name}" + fi } + if [ $# -lt 1 ] ; then exit 0 fi @@ -198,14 +204,8 @@ command=$1 shift case "${command}" in - start) - do_start $@ - ;; - setup_vm) - do_vm $@ - ;; - setup_computername) - do_computername $@ + init) + do_init $@ ;; esac diff --git a/launchd/src/register_mach_bootstrap_servers.c b/launchd/src/register_mach_bootstrap_servers.c deleted file mode 100644 index eb0d7cc..0000000 --- a/launchd/src/register_mach_bootstrap_servers.c +++ /dev/null @@ -1,169 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static void regServ(uid_t u, bool on_demand, bool is_kunc, const char *serv_name, const char *serv_cmd); -static void handleConfigFile(const char *file); -static CFPropertyListRef CreateMyPropertyListFromFile(const char *posixfile); - -int main(int argc, char *argv[]) -{ - DIR *d; - struct dirent *de; - struct stat sb; - - if (argc != 2) { - fprintf(stderr, "usage: %s: \n", getprogname()); - exit(EXIT_FAILURE); - } - - stat(argv[1], &sb); - - if (S_ISREG(sb.st_mode)) { - handleConfigFile(argv[1]); - exit(EXIT_SUCCESS); - } - - if (getenv("SECURITYSESSIONID")) { - if (fork() == 0) { - const char *h = getenv("HOME"); - struct passwd *pwe = getpwuid(getuid()); - char *buf; - asprintf(&buf, "%s/%s", h ? h : pwe->pw_dir, "Library/LaunchAgents"); - execlp("launchctl", "launchctl", "load", buf, "/Library/LaunchAgents", "/System/Library/LaunchAgents", NULL); - exit(EXIT_SUCCESS); - } - } - - if ((d = opendir(argv[1])) == NULL) { - fprintf(stderr, "%s: opendir() failed to open the directory\n", getprogname()); - exit(EXIT_FAILURE); - } - - while ((de = readdir(d)) != NULL) { - if ((de->d_name[0] != '.')) { - char *foo; - if (asprintf(&foo, "%s/%s", argv[1], de->d_name)) - handleConfigFile(foo); - free(foo); - } - } - - exit(EXIT_SUCCESS); -} - -static void handleConfigFile(const char *file) -{ - bool on_demand = true, is_kunc = false; - uid_t u = getuid(); - struct passwd *pwe; - char usr[4096]; - char serv_name[4096]; - char serv_cmd[4096]; - CFPropertyListRef plist = CreateMyPropertyListFromFile(file); - - if (plist) { - if (CFDictionaryContainsKey(plist, CFSTR("Username"))) { - const void *v = CFDictionaryGetValue(plist, CFSTR("Username")); - - if (v) CFStringGetCString(v, usr, sizeof(usr), kCFStringEncodingUTF8); - else goto out; - - if ((pwe = getpwnam(usr))) { - u = pwe->pw_uid; - } else { - fprintf(stderr, "%s: user not found\n", getprogname()); - goto out; - } - } - if (CFDictionaryContainsKey(plist, CFSTR("OnDemand"))) { - const void *v = CFDictionaryGetValue(plist, CFSTR("OnDemand")); - if (v) - on_demand = CFBooleanGetValue(v); - else goto out; - } - if (CFDictionaryContainsKey(plist, CFSTR("ServiceName"))) { - const void *v = CFDictionaryGetValue(plist, CFSTR("ServiceName")); - - if (v) CFStringGetCString(v, serv_name, sizeof(serv_name), kCFStringEncodingUTF8); - else goto out; - } - if (CFDictionaryContainsKey(plist, CFSTR("Command"))) { - const void *v = CFDictionaryGetValue(plist, CFSTR("Command")); - - if (v) CFStringGetCString(v, serv_cmd, sizeof(serv_cmd), kCFStringEncodingUTF8); - else goto out; - } - if (CFDictionaryContainsKey(plist, CFSTR("isKUNCServer"))) { - const void *v = CFDictionaryGetValue(plist, CFSTR("isKUNCServer")); - if (v && CFBooleanGetValue(v)) is_kunc = true; - else goto out; - } - regServ(u, on_demand, is_kunc, serv_name, serv_cmd); - goto out_good; -out: - fprintf(stdout, "%s: failed to register: %s\n", getprogname(), file); -out_good: - CFRelease(plist); - } else { - fprintf(stderr, "%s: no plist was returned for: %s\n", getprogname(), file); - } -} - -static void regServ(uid_t u, bool on_demand, bool is_kunc, const char *serv_name, const char *serv_cmd) -{ - kern_return_t kr; - mach_port_t msr, msv, mhp; - - if ((kr = bootstrap_create_server(bootstrap_port, (char*)serv_cmd, u, on_demand, &msr)) != KERN_SUCCESS) { - fprintf(stderr, "%s: bootstrap_create_server(): %d\n", getprogname(), kr); - return; - } - if ((kr = bootstrap_create_service(msr, (char*)serv_name, &msv)) != KERN_SUCCESS) { - fprintf(stderr, "%s: bootstrap_register(): %d\n", getprogname(), kr); - return; - } - if (is_kunc) { - mhp = mach_host_self(); - if ((kr = host_set_UNDServer(mhp, msv)) != KERN_SUCCESS) { - fprintf(stderr, "%s: host_set_UNDServer(): %s\n", getprogname(), mach_error_string(kr)); - return; - } - mach_port_deallocate(mach_task_self(), mhp); - } -} - -static CFPropertyListRef CreateMyPropertyListFromFile(const char *posixfile) -{ - CFPropertyListRef propertyList; - CFStringRef errorString; - CFDataRef resourceData; - SInt32 errorCode; - CFURLRef fileURL; - - fileURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8 *)posixfile, strlen(posixfile), false); - if (!fileURL) - fprintf(stderr, "%s: CFURLCreateFromFileSystemRepresentation(%s) failed\n", getprogname(), posixfile); - if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, fileURL, &resourceData, NULL, NULL, &errorCode)) - fprintf(stderr, "%s: CFURLCreateDataAndPropertiesFromResource(%s) failed: %d\n", getprogname(), posixfile, (int)errorCode); - propertyList = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, resourceData, kCFPropertyListImmutable, &errorString); - if (!propertyList) { - fprintf(stderr, "%s: propertyList is NULL\n", getprogname()); - if (errorString) - CFRelease(errorString); - } - if (resourceData) - CFRelease(resourceData); - if (fileURL) - CFRelease(fileURL); - - return propertyList; -} diff --git a/launchd/src/rpc_services.c b/launchd/src/rpc_services.c deleted file mode 100644 index 4336258..0000000 --- a/launchd/src/rpc_services.c +++ /dev/null @@ -1,846 +0,0 @@ -/* - * Copyright (c) 1999-2004 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights - * Reserved. This file contains Original Code and/or Modifications of - * Original Code as defined in and that are subject to the Apple Public - * Source License Version 1.0 (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. - * - * The 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@ - */ -/* - * bootstrap -- fundamental service initiator and port server - * Mike DeMoney, NeXT, Inc. - * Copyright, 1990. All rights reserved. - * - * rpc_services.c -- implementation of bootstrap rpc services - */ - -#include -#include -#include -#include - -#include -#include - -#include "bootstrap_internal.h" -#include "lists.h" -#include "bootstrap.h" - -#ifndef ASSERT -#define ASSERT(p) -#endif - -#ifndef NULL -#define NULL ((void *)0) -#endif NULL - -#define bsstatus(servicep) \ - (((servicep)->isActive) ? BOOTSTRAP_STATUS_ACTIVE : \ - (((servicep)->server && (servicep)->server->servertype == DEMAND) ? \ - BOOTSTRAP_STATUS_ON_DEMAND : BOOTSTRAP_STATUS_INACTIVE)) - -/* extern port_all_t backup_port; */ - -/* - * kern_return_t - * bootstrap_create_server(mach_port_t bootstrap_port, - * cmd_t server_cmd, - * integer_t server_uid, - * boolean_t on_demand, - * mach_port_t *server_portp) - * - * Returns send rights to server_port of service. At this point, the - * server appears active, so nothing will try to launch it. The server_port - * can be used to delare services associated with this server by calling - * bootstrap_create_service() and passing server_port as the bootstrap port. - * - * Errors: Returns appropriate kernel errors on rpc failure. - * Returns BOOTSTRAP_NOT_PRIVILEGED, if bootstrap port invalid. - */ -__private_extern__ kern_return_t -x_bootstrap_create_server( - mach_port_t bootstrapport, - cmd_t server_cmd, - uid_t server_uid, - boolean_t on_demand, - audit_token_t client_audit_token, - mach_port_t *server_portp) -{ - server_t *serverp; - struct auditinfo audit_info; - bootstrap_info_t *bootstrap; - - uid_t client_euid; - - bootstrap = lookup_bootstrap_by_port(bootstrapport); - syslog(LOG_DEBUG, "Server create attempt: \"%s\" bootstrap %x", - server_cmd, bootstrapport); - - /* No forwarding allowed for this call - security risk (we run as root) */ - if (!bootstrap || !active_bootstrap(bootstrap)) { - syslog(LOG_DEBUG, "Server create: \"%s\": invalid bootstrap %x", - server_cmd, bootstrapport); - return BOOTSTRAP_NOT_PRIVILEGED; - } - - /* get the identity of the requestor and set up audit_info of server */ - audit_token_to_au32(client_audit_token, - &audit_info.ai_auid, - &client_euid, - NULL /* egid */, - NULL /* ruid */, - NULL /* rgid */, - NULL /* pid */, - &audit_info.ai_asid, - &audit_info.ai_termid); - - if (client_euid != 0 && client_euid != server_uid) { - syslog(LOG_NOTICE, "Server create: \"%s\": insufficient privilege for specified uid (euid-%d != requested-%d)", - server_cmd, client_euid, server_uid); - return BOOTSTRAP_NOT_PRIVILEGED; - } - - serverp = new_server( - bootstrap, - server_cmd, - server_uid, - (on_demand) ? DEMAND : RESTARTABLE, - audit_info); - setup_server(serverp); - - syslog(LOG_INFO, "New server %x in bootstrap %x: \"%s\"", - serverp->port, bootstrapport, server_cmd); - *server_portp = serverp->port; - return BOOTSTRAP_SUCCESS; -} - -/* - * kern_return_t - * bootstrap_unprivileged(mach_port_t bootstrapport, - * mach_port_t *unprivportp) - * - * Given a bootstrap port, return its unprivileged equivalent. If - * the port is already unprivileged, another reference to the same - * port is returned. - * - * This is most often used by servers, which are launched with their - * bootstrap port set to the privileged port for the server, to get - * an unprivileged version of the same port for use by its unprivileged - * children (or any offspring that it does not want to count as part - * of the "server" for mach_init registration and re-launch purposes). - */ -__private_extern__ kern_return_t -x_bootstrap_unprivileged( - mach_port_t bootstrapport, - mach_port_t *unprivportp) -{ - bootstrap_info_t *bootstrap; - - syslog(LOG_DEBUG, "Get unprivileged attempt for bootstrap %x", bootstrapport); - - bootstrap = lookup_bootstrap_by_port(bootstrapport); - if (!bootstrap) { - syslog(LOG_DEBUG, "Get unprivileged: invalid bootstrap %x", bootstrapport); - return BOOTSTRAP_NOT_PRIVILEGED; - } - - *unprivportp = bootstrap->bootstrap_port; - - syslog(LOG_DEBUG, "Get unpriv bootstrap %x returned for bootstrap %x", - bootstrap->bootstrap_port, bootstrapport); - return BOOTSTRAP_SUCCESS; -} - - -/* - * kern_return_t - * bootstrap_check_in(mach_port_t bootstrapport, - * name_t servicename, - * mach_port_t *serviceportp) - * - * Returns receive rights to service_port of service named by service_name. - * - * Errors: Returns appropriate kernel errors on rpc failure. - * Returns BOOTSTRAP_UNKNOWN_SERVICE, if service does not exist. - * Returns BOOTSTRAP_SERVICE_NOT_DECLARED, if service not declared - * in /etc/bootstrap.conf. - * Returns BOOTSTRAP_SERVICE_ACTIVE, if service has already been - * registered or checked-in. - */ -__private_extern__ kern_return_t -x_bootstrap_check_in( - mach_port_t bootstrapport, - name_t servicename, - mach_port_t *serviceportp) -{ - kern_return_t result; - mach_port_t previous; - service_t *servicep; - server_t *serverp; - bootstrap_info_t *bootstrap; - - serverp = lookup_server_by_port(bootstrapport); - bootstrap = lookup_bootstrap_by_port(bootstrapport); - syslog(LOG_DEBUG, "Service checkin attempt for service %s bootstrap %x", - servicename, bootstrapport); - - servicep = lookup_service_by_name(bootstrap, servicename); - if (servicep == NULL || servicep->port == MACH_PORT_NULL) { - syslog(LOG_DEBUG, "bootstrap_check_in service %s unknown%s", servicename, - forward_ok ? " forwarding" : ""); - return forward_ok ? - bootstrap_check_in( - inherited_bootstrap_port, - servicename, - serviceportp) : - BOOTSTRAP_UNKNOWN_SERVICE; - } - if (servicep->server != NULL && servicep->server != serverp) { - syslog(LOG_DEBUG, "bootstrap_check_in service %s not privileged", - servicename); - return BOOTSTRAP_NOT_PRIVILEGED; - } - if (!canReceive(servicep->port)) { - ASSERT(servicep->isActive); - syslog(LOG_DEBUG, "bootstrap_check_in service %s already active", - servicename); - return BOOTSTRAP_SERVICE_ACTIVE; - } - syslog(LOG_DEBUG, "Checkin service %s for bootstrap %x", servicename, - bootstrap->bootstrap_port); - ASSERT(servicep->isActive == FALSE); - servicep->isActive = TRUE; - - if (servicep->server != NULL_SERVER) { - /* registered server - service needs backup */ - serverp->activity++; - serverp->active_services++; - result = mach_port_request_notification( - mach_task_self(), - servicep->port, - MACH_NOTIFY_PORT_DESTROYED, - 0, - backup_port, - MACH_MSG_TYPE_MAKE_SEND_ONCE, - &previous); - if (result != KERN_SUCCESS) - panic("mach_port_request_notification(): %s", mach_error_string(result)); - } else { - /* one time use/created service */ - servicep->servicetype = REGISTERED; - result = mach_port_request_notification( - mach_task_self(), - servicep->port, - MACH_NOTIFY_DEAD_NAME, - 0, - notify_port, - MACH_MSG_TYPE_MAKE_SEND_ONCE, - &previous); - if (result != KERN_SUCCESS) - panic("mach_port_request_notification(): %s", mach_error_string(result)); - else if (previous != MACH_PORT_NULL) { - syslog(LOG_DEBUG, "deallocating old notification port (%x) for checked in service %x", - previous, servicep->port); - result = mach_port_deallocate( - mach_task_self(), - previous); - if (result != KERN_SUCCESS) - panic("mach_port_deallocate(): %s", mach_error_string(result)); - } - } - - syslog(LOG_INFO, "Check-in service %x in bootstrap %x: %s", - servicep->port, servicep->bootstrap->bootstrap_port, servicep->name); - - *serviceportp = servicep->port; - return BOOTSTRAP_SUCCESS; -} - -/* - * kern_return_t - * bootstrap_register(mach_port_t bootstrapport, - * name_t servicename, - * mach_port_t serviceport) - * - * Registers send rights for the port service_port for the service named by - * service_name. Registering a declared service or registering a service for - * which bootstrap has receive rights via a port backup notification is - * allowed. - * The previous service port will be deallocated. Restarting services wishing - * to resume service for previous clients must first attempt to checkin to the - * service. - * - * Errors: Returns appropriate kernel errors on rpc failure. - * Returns BOOTSTRAP_NOT_PRIVILEGED, if request directed to - * unprivileged bootstrap port. - * Returns BOOTSTRAP_SERVICE_ACTIVE, if service has already been - * register or checked-in. - */ -__private_extern__ kern_return_t -x_bootstrap_register( - mach_port_t bootstrapport, - name_t servicename, - mach_port_t serviceport) -{ - kern_return_t result; - service_t *servicep; - server_t *serverp; - bootstrap_info_t *bootstrap; - mach_port_t old_port; - - syslog(LOG_DEBUG, "Register attempt for service %s port %x", - servicename, serviceport); - - /* - * Validate the bootstrap. - */ - bootstrap = lookup_bootstrap_by_port(bootstrapport); - if (!bootstrap || !active_bootstrap(bootstrap)) - return BOOTSTRAP_NOT_PRIVILEGED; - - /* - * If this bootstrap port is for a server, or it's an unprivileged - * bootstrap can't register the port. - */ - serverp = lookup_server_by_port(bootstrapport); - servicep = lookup_service_by_name(bootstrap, servicename); - if (servicep && servicep->server && servicep->server != serverp) - return BOOTSTRAP_NOT_PRIVILEGED; - - if (servicep == NULL || servicep->bootstrap != bootstrap) { - servicep = new_service(bootstrap, - servicename, - serviceport, - ACTIVE, - REGISTERED, - NULL_SERVER); - syslog(LOG_DEBUG, "Registered new service %s", servicename); - } else { - if (servicep->isActive) { - syslog(LOG_DEBUG, "Register: service %s already active, port %x", - servicep->name, servicep->port); - ASSERT(!canReceive(servicep->port)); - return BOOTSTRAP_SERVICE_ACTIVE; - } - old_port = servicep->port; - if (servicep->servicetype == DECLARED) { - servicep->servicetype = REGISTERED; - - if (servicep->server) { - ASSERT(servicep->server == serverp); - ASSERT(active_server(serverp)); - servicep->server = NULL_SERVER; - serverp->activity++; - } - - result = mach_port_mod_refs( - mach_task_self(), - old_port, - MACH_PORT_RIGHT_RECEIVE, - -1); - if (result != KERN_SUCCESS) - panic("mach_port_mod_refs(): %s", mach_error_string(result)); - } - result = mach_port_deallocate( - mach_task_self(), - old_port); - if (result != KERN_SUCCESS) - panic("mach_port_mod_refs(): %s", mach_error_string(result)); - - servicep->port = serviceport; - servicep->isActive = TRUE; - syslog(LOG_DEBUG, "Re-registered inactive service %x bootstrap %x: %s", - servicep->port, servicep->bootstrap->bootstrap_port, servicename); - } - - /* detect the new service port going dead */ - result = mach_port_request_notification( - mach_task_self(), - serviceport, - MACH_NOTIFY_DEAD_NAME, - 0, - notify_port, - MACH_MSG_TYPE_MAKE_SEND_ONCE, - &old_port); - if (result != KERN_SUCCESS) { - syslog(LOG_DEBUG, "Can't request notification on service %x bootstrap %x: %s", - service_port, servicep->bootstrap->bootstrap_port, "must be dead"); - delete_service(servicep); - return BOOTSTRAP_SUCCESS; - } else if (old_port != MACH_PORT_NULL) { - syslog(LOG_DEBUG, "deallocating old notification port (%x) for service %x", - old_port, serviceport); - result = mach_port_deallocate( - mach_task_self(), - old_port); - if (result != KERN_SUCCESS) - panic("mach_port_deallocate(): %s", mach_error_string(result)); - } - syslog(LOG_INFO, "Registered service %x bootstrap %x: %s", - servicep->port, servicep->bootstrap->bootstrap_port, servicep->name); - return BOOTSTRAP_SUCCESS; -} - -/* - * kern_return_t - * bootstrap_look_up(mach_port_t bootstrapport, - * name_t servicename, - * mach_port_t *serviceportp) - * - * Returns send rights for the service port of the service named by - * service_name in *service_portp. Service is not guaranteed to be active. - * - * Errors: Returns appropriate kernel errors on rpc failure. - * Returns BOOTSTRAP_UNKNOWN_SERVICE, if service does not exist. - */ -__private_extern__ kern_return_t -x_bootstrap_look_up( - mach_port_t bootstrapport, - name_t servicename, - mach_port_t *serviceportp) -{ - service_t *servicep; - bootstrap_info_t *bootstrap; - - bootstrap = lookup_bootstrap_by_port(bootstrapport); - servicep = lookup_service_by_name(bootstrap, servicename); - if (servicep == NULL || servicep->port == MACH_PORT_NULL) { - if (forward_ok) { - syslog(LOG_DEBUG, "bootstrap_look_up service %s forwarding", - servicename); - return bootstrap_look_up(inherited_bootstrap_port, - servicename, - serviceportp); - } else { - syslog(LOG_DEBUG, "bootstrap_look_up service %s unknown", - servicename); - return BOOTSTRAP_UNKNOWN_SERVICE; - } - } - *serviceportp = servicep->port; - syslog(LOG_DEBUG, "Lookup returns port %x for service %s", servicep->port, servicep->name); - return BOOTSTRAP_SUCCESS; -} - -/* - * kern_return_t - * bootstrap_look_up_array(mach_port_t bootstrapport, - * name_array_t servicenames, - * int servicenames_cnt, - * mach_port_array_t *serviceports, - * int *serviceports_cnt, - * boolean_t *allservices_known) - * - * Returns port send rights in corresponding entries of the array service_ports - * for all services named in the array service_names. Service_ports_cnt is - * returned and will always equal service_names_cnt (assuming service_names_cnt - * is greater than or equal to zero). - * - * Errors: Returns appropriate kernel errors on rpc failure. - * Returns BOOTSTRAP_NO_MEMORY, if server couldn't obtain memory - * for response. - * Unknown service names have the corresponding service - * port set to MACH_PORT_NULL. - * If all services are known, all_services_known is true on - * return, - * if any service is unknown, it's false. - */ -__private_extern__ kern_return_t -x_bootstrap_look_up_array( - mach_port_t bootstrapport, - name_array_t servicenames, - unsigned int servicenames_cnt, - mach_port_array_t *serviceportsp, - unsigned int *serviceports_cnt, - boolean_t *allservices_known) -{ - unsigned int i; - static mach_port_t service_ports[BOOTSTRAP_MAX_LOOKUP_COUNT]; - - if (servicenames_cnt > BOOTSTRAP_MAX_LOOKUP_COUNT) - return BOOTSTRAP_BAD_COUNT; - *serviceports_cnt = servicenames_cnt; - *allservices_known = TRUE; - for (i = 0; i < servicenames_cnt; i++) { - if ( x_bootstrap_look_up(bootstrapport, - servicenames[i], - &service_ports[i]) - != BOOTSTRAP_SUCCESS) - { - *allservices_known = FALSE; - service_ports[i] = MACH_PORT_NULL; - } - } - syslog(LOG_DEBUG, "bootstrap_look_up_array returns %d ports", servicenames_cnt); - *serviceportsp = service_ports; - return BOOTSTRAP_SUCCESS; -} - -/* - * kern_return_t - * bootstrap_parent(mach_port_t bootstrapport, - * mach_port_t *parentport); - * - * Given a bootstrap subset port, return the parent bootstrap port. - * If the specified bootstrap port is already the root subset, - * MACH_PORT_NULL will be returned. - * - * Errors: - * Returns BOOTSTRAP_NOT_PRIVILEGED if the caller is not running - * with an effective user id of root (as determined by the security - * token in the message trailer). - */ -__private_extern__ kern_return_t -x_bootstrap_parent( - mach_port_t bootstrapport, - security_token_t sectoken, - mach_port_t *parentport) -{ - bootstrap_info_t *bootstrap; - - syslog(LOG_DEBUG, "Parent attempt for bootstrap %x", bootstrapport); - - bootstrap = lookup_bootstrap_by_port(bootstrapport); - if (!bootstrap) { - syslog(LOG_DEBUG, "Parent attempt for bootstrap %x: invalid bootstrap", - bootstrapport); - return BOOTSTRAP_NOT_PRIVILEGED; - } - if (sectoken.val[0]) { - syslog(LOG_NOTICE, "Bootstrap parent for bootstrap %x: invalid security token (%d)", - bootstrapport, sectoken.val[0]); - return BOOTSTRAP_NOT_PRIVILEGED; - } - syslog(LOG_DEBUG, "Returning bootstrap parent %x for bootstrap %x", - bootstrap->parent->bootstrap_port, bootstrapport); - *parentport = bootstrap->parent->bootstrap_port; - return BOOTSTRAP_SUCCESS; -} - -/* - * kern_return_t - * bootstrap_status(mach_port_t bootstrapport, - * name_t servicename, - * bootstrap_status_t *serviceactive); - * - * Returns: service_active indicates if service is available. - * - * Errors: Returns appropriate kernel errors on rpc failure. - * Returns BOOTSTRAP_UNKNOWN_SERVICE, if service does not exist. - */ -__private_extern__ kern_return_t -x_bootstrap_status( - mach_port_t bootstrapport, - name_t servicename, - bootstrap_status_t *serviceactivep) -{ - service_t *servicep; - bootstrap_info_t *bootstrap; - - bootstrap = lookup_bootstrap_by_port(bootstrapport); - servicep = lookup_service_by_name(bootstrap, servicename); - if (servicep == NULL) { - if (forward_ok) { - syslog(LOG_DEBUG, "bootstrap_status forwarding status, server %s", - servicename); - return bootstrap_status(inherited_bootstrap_port, - servicename, - serviceactivep); - } else { - syslog(LOG_DEBUG, "bootstrap_status service %s unknown", - servicename); - return BOOTSTRAP_UNKNOWN_SERVICE; - } - } - *serviceactivep = bsstatus(servicep); - - syslog(LOG_DEBUG, "bootstrap_status server %s %sactive", servicename, - servicep->isActive ? "" : "in"); - return BOOTSTRAP_SUCCESS; -} - -/* - * kern_return_t - * bootstrap_info(mach_port_t bootstrapport, - * name_array_t *servicenamesp, - * int *servicenames_cnt, - * name_array_t *servernamesp, - * int *servernames_cnt, - * bootstrap_status_array_t *serviceactivesp, - * int *serviceactive_cnt); - * - * Returns bootstrap status for all known services. - * - * Errors: Returns appropriate kernel errors on rpc failure. - */ -__private_extern__ kern_return_t -x_bootstrap_info( - mach_port_t bootstrapport, - name_array_t *servicenamesp, - unsigned int *servicenames_cnt, - name_array_t *servernamesp, - unsigned int *servernames_cnt, - bootstrap_status_array_t *serviceactivesp, - unsigned int *serviceactives_cnt) -{ - kern_return_t result; - unsigned int i, cnt; - service_t *servicep; - server_t *serverp; - bootstrap_info_t *bootstrap; - name_array_t service_names; - name_array_t server_names; - bootstrap_status_array_t service_actives; - - bootstrap = lookup_bootstrap_by_port(bootstrapport); - - for ( cnt = i = 0, servicep = services.next - ; i < nservices - ; servicep = servicep->next, i++) - { - if (lookup_service_by_name(bootstrap, servicep->name) == servicep) - { - cnt++; - } - } - result = vm_allocate(mach_task_self(), - (vm_address_t *)&service_names, - cnt * sizeof(service_names[0]), - ANYWHERE); - if (result != KERN_SUCCESS) - return BOOTSTRAP_NO_MEMORY; - - result = vm_allocate(mach_task_self(), - (vm_address_t *)&server_names, - cnt * sizeof(server_names[0]), - ANYWHERE); - if (result != KERN_SUCCESS) { - (void)vm_deallocate(mach_task_self(), - (vm_address_t)service_names, - cnt * sizeof(service_names[0])); - return BOOTSTRAP_NO_MEMORY; - } - result = vm_allocate(mach_task_self(), - (vm_address_t *)&service_actives, - cnt * sizeof(service_actives[0]), - ANYWHERE); - if (result != KERN_SUCCESS) { - (void)vm_deallocate(mach_task_self(), - (vm_address_t)service_names, - cnt * sizeof(service_names[0])); - (void)vm_deallocate(mach_task_self(), - (vm_address_t)server_names, - cnt * sizeof(server_names[0])); - return BOOTSTRAP_NO_MEMORY; - } - - for ( i = 0, servicep = services.next - ; i < cnt - ; servicep = servicep->next) - { - if ( lookup_service_by_name(bootstrap, servicep->name) - != servicep) - continue; - strncpy(service_names[i], - servicep->name, - sizeof(service_names[0])); - service_names[i][sizeof(service_names[0]) - 1] = '\0'; - if (servicep->server) { - serverp = servicep->server; - strncpy(server_names[i], - serverp->cmd, - sizeof(server_names[0])); - server_names[i][sizeof(server_names[0]) - 1] = '\0'; - syslog(LOG_DEBUG, "bootstrap info service %s server %s %sactive", - servicep->name, - serverp->cmd, servicep->isActive ? "" : "in"); - } else { - server_names[i][0] = '\0'; - syslog(LOG_DEBUG, "bootstrap info service %s %sactive", - servicep->name, servicep->isActive ? "" : "in"); - } - service_actives[i] = bsstatus(servicep); - i++; - } - *servicenamesp = service_names; - *servernamesp = server_names; - *serviceactivesp = service_actives; - *servicenames_cnt = *servernames_cnt = *serviceactives_cnt = cnt; - - return BOOTSTRAP_SUCCESS; -} - -/* - * kern_return_t - * bootstrap_subset(mach_port_t bootstrapport, - * mach_port_t requestorport, - * mach_port_t *subsetport); - * - * Returns a new port to use as a bootstrap port. This port behaves - * exactly like the previous bootstrap_port, except that ports dynamically - * registered via bootstrap_register() are available only to users of this - * specific subset_port. Lookups on the subset_port will return ports - * registered with this port specifically, and ports registered with - * ancestors of this subset_port. Duplications of services already - * registered with an ancestor port may be registered with the subset port - * are allowed. Services already advertised may then be effectively removed - * by registering MACH_PORT_NULL for the service. - * When it is detected that the requestor_port is destroied the subset - * port and all services advertized by it are destroied as well. - * - * Errors: Returns appropriate kernel errors on rpc failure. - */ -__private_extern__ kern_return_t -x_bootstrap_subset( - mach_port_t bootstrapport, - mach_port_t requestorport, - mach_port_t *subsetportp) -{ - kern_return_t result; - bootstrap_info_t *bootstrap; - bootstrap_info_t *subset; - mach_port_t new_bootstrapport; - mach_port_t previous; - - syslog(LOG_DEBUG, "Subset create attempt: bootstrap %x, requestor: %x", - bootstrapport, requestorport); - - bootstrap = lookup_bootstrap_by_port(bootstrapport); - if (!bootstrap || !active_bootstrap(bootstrap)) - return BOOTSTRAP_NOT_PRIVILEGED; - - result = mach_port_allocate( - mach_task_self(), - MACH_PORT_RIGHT_RECEIVE, - &new_bootstrapport); - if (result != KERN_SUCCESS) - panic("mach_port_allocate(): %s", mach_error_string(result)); - - result = mach_port_insert_right( - mach_task_self(), - new_bootstrapport, - new_bootstrapport, - MACH_MSG_TYPE_MAKE_SEND); - if (result != KERN_SUCCESS) - panic("failed to insert send right(): %s", mach_error_string(result)); - - result = mach_port_insert_member( - mach_task_self(), - new_bootstrapport, - bootstrap_port_set); - if (result != KERN_SUCCESS) - panic("port_set_add(): %s", mach_error_string(result)); - - subset = new_bootstrap(bootstrap, new_bootstrapport, requestorport); - - result = mach_port_request_notification( - mach_task_self(), - requestorport, - MACH_NOTIFY_DEAD_NAME, - 0, - notify_port, - MACH_MSG_TYPE_MAKE_SEND_ONCE, - &previous); - if (result != KERN_SUCCESS) { - syslog(LOG_ERR, "mach_port_request_notification(): %s", mach_error_string(result)); - mach_port_deallocate(mach_task_self(), requestorport); - subset->requestor_port = MACH_PORT_NULL; - deactivate_bootstrap(subset); - } else if (previous != MACH_PORT_NULL) { - syslog(LOG_DEBUG, "deallocating old notification port (%x) for requestor %x", - previous, requestorport); - result = mach_port_deallocate( - mach_task_self(), - previous); - if (result != KERN_SUCCESS) - panic("mach_port_deallocate(): %s", mach_error_string(result)); - } - - syslog(LOG_INFO, "Created bootstrap subset %x parent %x requestor %x", - new_bootstrapport, bootstrapport, requestorport); - *subsetportp = new_bootstrapport; - return BOOTSTRAP_SUCCESS; -} - -/* - * kern_return_t - * bootstrap_create_service(mach_port_t bootstrapport, - * name_t servicename, - * mach_port_t *serviceportp) - * - * Creates a service named "service_name" and returns send rights to that - * port in "service_port." The port may later be checked in as if this - * port were configured in the bootstrap configuration file. - * - * Errors: Returns appropriate kernel errors on rpc failure. - * Returns BOOTSTRAP_NAME_IN_USE, if service already exists. - */ -__private_extern__ kern_return_t -x_bootstrap_create_service( - mach_port_t bootstrapport, - name_t servicename, - mach_port_t *serviceportp) -{ - server_t *serverp; - service_t *servicep; - bootstrap_info_t *bootstrap; - kern_return_t result; - - bootstrap = lookup_bootstrap_by_port(bootstrapport); - if (!bootstrap || !active_bootstrap(bootstrap)) - return BOOTSTRAP_NOT_PRIVILEGED; - - syslog(LOG_DEBUG, "Service creation attempt for service %s bootstrap %x", - servicename, bootstrapport); - - servicep = lookup_service_by_name(bootstrap, servicename); - if (servicep) { - syslog(LOG_DEBUG, "Service creation attempt for service %s failed, " - "service already exists", servicename); - return BOOTSTRAP_NAME_IN_USE; - } - - serverp = lookup_server_by_port(bootstrapport); - - result = mach_port_allocate(mach_task_self(), - MACH_PORT_RIGHT_RECEIVE, - serviceportp); - if (result != KERN_SUCCESS) - panic("port_allocate(): %s", mach_error_string(result)); - result = mach_port_insert_right(mach_task_self(), - *serviceportp, - *serviceportp, - MACH_MSG_TYPE_MAKE_SEND); - if (result != KERN_SUCCESS) - panic("failed to insert send right(): %s", mach_error_string(result)); - - if (serverp) - serverp->activity++; - - servicep = new_service(bootstrap, - servicename, - *serviceportp, - !ACTIVE, - DECLARED, - serverp); - - syslog(LOG_INFO, "Created new service %x in bootstrap %x: %s", - servicep->port, bootstrap->bootstrap_port, servicename); - - return BOOTSTRAP_SUCCESS; -} diff --git a/launchd/src/service b/launchd/src/service index be42d2b..97cdf46 100755 --- a/launchd/src/service +++ b/launchd/src/service @@ -1,5 +1,7 @@ #!/bin/sh +unset LAUNCHD_SOCKET + set -e # don't let people kill us. We shouldn't be long, so this isn't a big deal. diff --git a/launchd/src/wait4path.c b/launchd/src/wait4path.c index ee1c1ae..1f942e5 100644 --- a/launchd/src/wait4path.c +++ b/launchd/src/wait4path.c @@ -1,24 +1,21 @@ /* * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ + * @APPLE_APACHE_LICENSE_HEADER_START@ * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * The 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, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and * limitations under the License. * - * @APPLE_LICENSE_HEADER_END@ + * @APPLE_APACHE_LICENSE_HEADER_END@ */ #include #include diff --git a/launchd/testing/EVFILT_TIMER.c b/launchd/testing/EVFILT_TIMER.c new file mode 100644 index 0000000..5248bdb --- /dev/null +++ b/launchd/testing/EVFILT_TIMER.c @@ -0,0 +1,50 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +static int kq = -1; + +time_t +now_plus_n(int n) +{ + time_t later = time(NULL); + + return later + n; +} + +void +add_abs_timer(int n) +{ + struct kevent kev; + + EV_SET(&kev, n * 100000, EVFILT_TIMER, EV_ADD, NOTE_SECONDS | NOTE_ABSOLUTE, now_plus_n(n), (void *)n); + + assert(kevent(kq, &kev, 1, NULL, 0, NULL) == 0); +} + +int +main(void) +{ + struct kevent kev; + + assert((kq = kqueue()) != -1); + + add_abs_timer(2); + add_abs_timer(3); + add_abs_timer(4); + add_abs_timer(5); + add_abs_timer(6); + + for (;;) { + assert(kevent(kq, NULL, 0, &kev, 1, NULL) == 1); + fprintf(stdout, "kev.ident == %ld kev.udata == %p\n", kev.ident, kev.udata); + add_abs_timer((int)kev.udata); + } + + exit(EXIT_SUCCESS); +} diff --git "a/launchd/testing/Qu\304\261c\304\270s\304\261lv\316\265\312\200" "b/launchd/testing/Qu\304\261c\304\270s\304\261lv\316\265\312\200" new file mode 100755 index 0000000..a54a10e --- /dev/null +++ "b/launchd/testing/Qu\304\261c\304\270s\304\261lv\316\265\312\200" @@ -0,0 +1,3 @@ +#!/bin/sh + +echo "UTF-8 is fun!" diff --git a/launchd/testing/loginwindow_helper_code.c b/launchd/testing/loginwindow_helper_code.c new file mode 100644 index 0000000..ee529e9 --- /dev/null +++ b/launchd/testing/loginwindow_helper_code.c @@ -0,0 +1,38 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int main(void) +{ + mach_port_t oldbsport = bootstrap_port; + pid_t p; + + if (getuid() != 0) { + fprintf(stderr, "This test tool must be run as root.\n"); + exit(EXIT_FAILURE); + } + + p = create_and_switch_to_per_session_launchd("www"); + + if (p > 0) { + fprintf(stdout, "Our PID: %d\n", getpid()); + fprintf(stdout, "Per session launchd PID: %d\n", p); + fprintf(stdout, "Old bootstrap port: 0x%x\n", oldbsport); + fprintf(stdout, "New bootstrap port: 0x%x\n", bootstrap_port); + for (;;) { + pause(); + } + } else if (p == -1) { + fprintf(stderr, "create_and_switch_to_per_session_launchd() failed: %s\n", strerror(errno)); + } else if (p == 0) { + fprintf(stderr, "create_and_switch_to_per_session_launchd() returned zero?!?\n"); + } + exit(EXIT_FAILURE); +} diff --git a/launchd/testing/mach_via_launchd.c b/launchd/testing/mach_via_launchd.c new file mode 100644 index 0000000..851db4b --- /dev/null +++ b/launchd/testing/mach_via_launchd.c @@ -0,0 +1,47 @@ +#include +#include +#include +#include +#include +#include + +void print_mach_service(launch_data_t obj, const char *key, void *context) +{ + if (launch_data_get_type(obj) == LAUNCH_DATA_MACHPORT) { + fprintf(stdout, "%s: %d\n", key, launch_data_get_machport(obj)); + mach_port_deallocate(mach_task_self(), launch_data_get_machport(obj)); + mach_port_mod_refs(mach_task_self(), launch_data_get_machport(obj), MACH_PORT_RIGHT_RECEIVE, -1); + } else { + fprintf(stdout, "%s: not a mach port\n", key); + } +} + +int main(void) +{ + launch_data_t resp, tmp, msg = launch_data_new_string(LAUNCH_KEY_CHECKIN); + + resp = launch_msg(msg); + + if (resp == NULL) { + fprintf(stderr, "launch_msg(): %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + + if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) { + errno = launch_data_get_errno(resp); + fprintf(stderr, "launch_msg() response: %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + + tmp = launch_data_dict_lookup(resp, LAUNCH_JOBKEY_MACHSERVICES); + + if (tmp == NULL) { + fprintf(stderr, "no mach services found!\n"); + exit(EXIT_FAILURE); + } + + launch_data_dict_iterate(tmp, print_mach_service, NULL); + + sleep(1); + exit(EXIT_SUCCESS); +} diff --git a/launchd/testing/mach_via_launchd.plist b/launchd/testing/mach_via_launchd.plist new file mode 100644 index 0000000..e92df3e --- /dev/null +++ b/launchd/testing/mach_via_launchd.plist @@ -0,0 +1,19 @@ + + + + + Label + launchd.testing.mach_via_launchd + ProgramArguments + + ./mach_via_launchd + + OnDemand + + MachServices + + com.apple.launchd.test.service + + + + diff --git a/launchd/testing/missed-EVFILT_READ.c b/launchd/testing/missed-EVFILT_READ.c new file mode 100644 index 0000000..042a551 --- /dev/null +++ b/launchd/testing/missed-EVFILT_READ.c @@ -0,0 +1,112 @@ +/* + * EVFILT_READ doesn't fire reliably + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void do_parent(int thefd); +static void do_child(int thefd); + +int main(void) +{ + int sp[2]; + + if (-1 == socketpair(AF_UNIX, SOCK_STREAM, 0, sp)) { + fprintf(stderr, "socketpair(): %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + + switch (fork()) { + case -1: + fprintf(stderr, "fork(): %s\n", strerror(errno)); + exit(EXIT_FAILURE); + case 0: + close(sp[0]); + do_child(sp[1]); + break; + default: + close(sp[1]); + do_parent(sp[0]); + break; + } + + exit(EXIT_SUCCESS); +} + +void +do_child(int thefd) +{ + char junk = '\0'; + + for (;;) { + if (-1 == write(thefd, &junk, sizeof(junk))) { + fprintf(stderr, "%d: write(): %s\n", __LINE__, strerror(errno)); + exit(EXIT_FAILURE); + } + if (-1 == read(thefd, &junk, sizeof(junk))) { + fprintf(stderr, "%d: read(): %s\n", __LINE__, strerror(errno)); + exit(EXIT_FAILURE); + } + } +} + +void +do_parent(int thefd) +{ + struct timespec timeout = { 5, 0 }; + int iter = 0, kq; + struct kevent kev; + char junk = '\0'; + + if (-1 == (kq = kqueue())) { + fprintf(stderr, "kqueue(): %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + + EV_SET(&kev, thefd, EVFILT_READ, EV_ADD, 0, 0, NULL); + + if (-1 == kevent(kq, &kev, 1, NULL, 0, NULL)) { + fprintf(stderr, "%d: kevent(): %s\n", __LINE__, strerror(errno)); + exit(EXIT_FAILURE); + } + + for (;;) { + switch (kevent(kq, NULL, 0, &kev, 1, &timeout)) { + case -1: + fprintf(stderr, "%d: kevent(): %s\n", __LINE__, strerror(errno)); + exit(EXIT_FAILURE); + case 0: + fprintf(stderr, "After %d iterations, 4437060 still exists!\n", iter); + exit(EXIT_FAILURE); + case 1: + break; + default: + fprintf(stderr, "kevent should only return -1, 0 or 1 for this case!\n"); + exit(EXIT_FAILURE); + } + + if (kev.filter != EVFILT_READ) { + fprintf(stderr, "kevent should return EVFILT_READ!\n"); + exit(EXIT_FAILURE); + } + + if (-1 == read(thefd, &junk, sizeof(junk))) { + fprintf(stderr, "read(): %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + if (-1 == write(thefd, &junk, sizeof(junk))) { + fprintf(stderr, "%d: write(): %s\n", __LINE__, strerror(errno)); + exit(EXIT_FAILURE); + } + iter++; + } +} diff --git a/launchd/testing/missed-fds.c b/launchd/testing/missed-fds.c new file mode 100644 index 0000000..24577b5 --- /dev/null +++ b/launchd/testing/missed-fds.c @@ -0,0 +1,186 @@ +/* + * 8G1153: Cannot SSH into machine despite Remote Login being checked + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void do_parent(int thefd); +static void do_child(int thefd); + +int main(void) +{ + int sp[2]; + pid_t p; + + assert(socketpair(AF_UNIX, SOCK_STREAM, 0, sp) != -1); + + assert((p = fork()) != -1); + + if (p == 0) { + assert(close(sp[0]) != -1); + do_child(sp[1]); + } else { + assert(close(sp[1]) != -1); + do_parent(sp[0]); + } + + exit(EXIT_SUCCESS); +} + +static int +alloc_random_fd(void) +{ + struct sockaddr_in ina; + int fd; + + memset(&ina, 0, sizeof(ina)); + ina.sin_family = AF_INET; + assert((fd = socket(PF_INET, SOCK_STREAM, 0)) != -1); + assert(bind(fd, (struct sockaddr *)&ina, sizeof(ina)) != -1); + assert(listen(fd, SOMAXCONN) != -1); + + return fd; +} + +static int total_fds_sent = 0; + +void +send_fds(int thefd) +{ + struct cmsghdr *cm = NULL; + struct msghdr mh; + struct iovec iov; + size_t sentctrllen = 0; + int fdcnt = (rand() % 223) + 1; /* 223 is prime */ + int r, i, fds[fdcnt]; + + memset(&mh, 0, sizeof(mh)); + + iov.iov_base = &fdcnt; + iov.iov_len = sizeof(fdcnt); + mh.msg_iov = &iov; + mh.msg_iovlen = 1; + + for (i = 0; i < fdcnt; i++) { + fds[i] = alloc_random_fd(); + } + + sentctrllen = mh.msg_controllen = CMSG_SPACE(fdcnt * sizeof(int)); + + mh.msg_control = cm = alloca(mh.msg_controllen); + + memset(cm, 0, mh.msg_controllen); + + cm->cmsg_len = CMSG_LEN(fdcnt * sizeof(int)); + cm->cmsg_level = SOL_SOCKET; + cm->cmsg_type = SCM_RIGHTS; + + memcpy(CMSG_DATA(cm), fds, fdcnt * sizeof(int)); + + if (sendmsg(thefd, &mh, 0) == -1) { + fprintf(stderr, "Child: sendmsg(): %s\n", strerror(errno)); + fprintf(stderr, "Child: Tried to send %d fds\n", fdcnt); + fprintf(stderr, "Child: Total FDs sent: %d\n", total_fds_sent); + sleep(1); + exit(EXIT_FAILURE); + } + total_fds_sent += fdcnt; + + assert(sentctrllen == mh.msg_controllen); + + r = read(thefd, &i, sizeof(i)); + assert(r != -1); + assert(r != 0); + + for (i = 0; i < fdcnt; i++) { + assert(close(fds[i]) != -1); + } +} + +void +do_child(int thefd) +{ + for (;;) { + send_fds(thefd); + } +} + +static int total_fds_received = 0; + +static bool +fetch_and_check_fds(int thefd) +{ + struct cmsghdr *cm = alloca(4096); + struct msghdr mh; + struct iovec iov; + int r, i, *fds, fdcnt = 0, sentfds; + + memset(&mh, 0, sizeof(mh)); + + iov.iov_base = &fdcnt; + iov.iov_len = sizeof(fdcnt); + mh.msg_iov = &iov; + mh.msg_iovlen = 1; + mh.msg_control = cm; + mh.msg_controllen = 4096; + + r = recvmsg(thefd, &mh, 0); + assert(r != -1); + assert(r != 0); + assert(!(mh.msg_flags & MSG_CTRUNC)); + assert(mh.msg_controllen > 0); + + fds = (int *)CMSG_DATA(cm); + sentfds = (mh.msg_controllen - sizeof(struct cmsghdr)) / sizeof(int); + + if (sentfds != fdcnt) { + fprintf(stderr, "%d FDs sent, %d actually received.\n", fdcnt, sentfds); + return false; + } + + total_fds_received += fdcnt; + + for (i = 0; i < fdcnt; i++) { + assert(close(fds[i]) != -1); + } + + r = write(thefd, &fdcnt, sizeof(fdcnt)); + assert(r != -1); + assert(r != 0); + + return true; +} + +void +do_parent(int thefd) +{ + struct kevent kev; + int kq, iter = 0; + + EV_SET(&kev, thefd, EVFILT_READ, EV_ADD, 0, 0, NULL); + + assert((kq = kqueue()) != -1); + assert(kevent(kq, &kev, 1, NULL, 0, NULL) != -1); + + for (iter = 0; ; iter++) { + assert(kevent(kq, NULL, 0, &kev, 1, NULL) == 1); + assert(kev.filter == EVFILT_READ); + if (!fetch_and_check_fds(thefd)) + break; + } + + fprintf(stderr, "After %d iterations and %d FDs received, bug 4389914 still exists!\n", iter, total_fds_received); + exit(EXIT_FAILURE); +} diff --git a/launchd/testing/spawn_via_launchd.c b/launchd/testing/spawn_via_launchd.c new file mode 100644 index 0000000..5df7f94 --- /dev/null +++ b/launchd/testing/spawn_via_launchd.c @@ -0,0 +1,53 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int +main(int argc, const char *const *argv) +{ + struct timeval tvs, tve, tvd; + mach_port_t mpo = MACH_PORT_NULL; + int wstatus; + pid_t p; + + struct spawn_via_launchd_attr attrs; + memset(&attrs, 0, sizeof(attrs)); + attrs.spawn_flags = SPAWN_VIA_LAUNCHD_STOPPED; // | SPAWN_VIA_LAUNCHD_FORCE_PPC; + attrs.spawn_observer_port = &mpo; +#if 0 + const char *const env[] = { "FOO=bar", "ROCK=roll", NULL }; + attrs.spawn_path = "/bin/booger"; + attrs.spawn_chdir = "/Users/me"; + attrs.spawn_env = env; +#endif + + gettimeofday(&tvs, NULL); + p = spawn_via_launchd("com.example.booger", argv + 1, &attrs); + + if (p == -1) { + fprintf(stderr, "spawn_via_launchd(): %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + + kill(p, SIGCONT); + gettimeofday(&tve, NULL); + timersub(&tve, &tvs, &tvd); + fprintf(stdout, "p == %d mpo = 0x%x in %ld usec\n", p, mpo, tvd.tv_sec * 1000000 + tvd.tv_usec); + + assert(mpm_wait(mpo, &wstatus) == 0); + + fprintf(stdout, "wstatus == %d\n", wstatus); + + exit(EXIT_SUCCESS); +} -- 2.45.2