aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKlaatu <[email protected]>2015-05-17 15:33:21 +1200
committerKlaatu <[email protected]>2015-05-17 15:33:21 +1200
commitb0de699679e8f1e39af847ed172d1ba605b4370c (patch)
tree01dac00471d61f727394e508c613b29cff0ceae5
bulk upload of source
-rw-r--r--00THANKS6
-rw-r--r--CHANGES129
-rw-r--r--COPYING340
-rw-r--r--Makefile221
-rw-r--r--README213
-rw-r--r--S/atoulong.S17
-rw-r--r--S/byte_copy.S23
-rw-r--r--S/byte_copyr.S22
-rw-r--r--S/byte_diff.S19
-rw-r--r--S/byte_set.S5
-rw-r--r--S/byte_zero.S16
-rw-r--r--S/str_chr.S17
-rw-r--r--S/str_copy.S18
-rw-r--r--S/str_copyn.S22
-rw-r--r--S/str_diff.S22
-rw-r--r--S/str_diffn.S26
-rw-r--r--S/str_len.S11
-rw-r--r--S/str_rchr.S24
-rw-r--r--addprocess.h30
-rw-r--r--check_opt.h8
-rw-r--r--contrib/argv0.c13
l---------contrib/bootlog.c1
-rw-r--r--contrib/bootlog.h100
-rw-r--r--contrib/bootlog_mmap.c96
-rw-r--r--contrib/bootlog_sbrk.c73
-rw-r--r--contrib/conditional-init.c47
-rw-r--r--contrib/env.c68
-rw-r--r--contrib/put_env.h27
-rw-r--r--contrib/serdo.c236
-rw-r--r--contrib/sleeprun.c65
-rw-r--r--cron.c27
-rw-r--r--djb/README2
-rw-r--r--djb/atoulong.c7
-rw-r--r--djb/buffer.h16
-rw-r--r--djb/buffer_get.c17
-rw-r--r--djb/buffer_put.c37
-rw-r--r--djb/buffer_putc.c5
-rw-r--r--djb/buffer_puts.c6
-rw-r--r--djb/byte_copy.c7
-rw-r--r--djb/byte_copyr.c8
-rw-r--r--djb/byte_diff.c11
-rw-r--r--djb/byte_set.c7
-rw-r--r--djb/byte_zero.c7
-rw-r--r--djb/env_get.c15
-rw-r--r--djb/fmt_str.c8
-rw-r--r--djb/fmt_ulong.c10
-rw-r--r--djb/scan_8ulong.c5
-rw-r--r--djb/scan_number.h9
-rw-r--r--djb/scan_ulong.c5
-rw-r--r--djb/str_chr.c6
-rw-r--r--djb/str_copy.c5
-rw-r--r--djb/str_copyn.c6
-rw-r--r--djb/str_diff.c11
-rw-r--r--djb/str_diffn.c11
-rw-r--r--djb/str_len.c5
-rw-r--r--djb/str_rchr.c9
-rw-r--r--error_table.h19
-rw-r--r--findservice.h24
-rwxr-xr-xget_headers25
-rw-r--r--get_services.h91
-rw-r--r--home.tar.gzbin0 -> 691 bytes
-rw-r--r--initreq.h77
-rw-r--r--inittab.c374
-rw-r--r--install-bin.c129
-rw-r--r--lib/do_wtmp.c11
-rw-r--r--lib/err.c39
-rw-r--r--lib/err_b.c5
-rw-r--r--lib/errmsg_argv0.c1
-rw-r--r--lib/errmsg_put.c20
-rw-r--r--lib/errmsg_puts.c5
-rw-r--r--lib/error_string.c16
-rw-r--r--lib/fu.c16
-rw-r--r--lib/nano_sleep.c6
-rw-r--r--lib/open_tmpfd.c18
-rw-r--r--lib/pathexec_run.c43
-rw-r--r--lib/read_header.c18
-rw-r--r--lib/read_ulongs.c8
-rw-r--r--lib/scan_sec.c23
-rw-r--r--lib/scan_ulongs.c17
-rw-r--r--lib/skip_comments.c11
-rw-r--r--lib/splitmem.c18
-rw-r--r--lib/strpbrk.c10
-rw-r--r--lib/strstr.c16
-rw-r--r--lib/utmp_io.c29
-rw-r--r--lib/x_atoi.c12
-rw-r--r--library_files52
-rw-r--r--man/bootlog.889
-rw-r--r--man/inittab.898
-rw-r--r--man/ninit.8178
-rw-r--r--man/nkillall.8139
-rw-r--r--man/nsvc.8240
-rw-r--r--man/pidfile.875
-rw-r--r--man/pututmpid.873
-rw-r--r--man/reboot.859
-rw-r--r--man/reload.8120
-rw-r--r--man/runlevel.8108
-rw-r--r--man/scan.899
-rw-r--r--man/service.846
-rw-r--r--man/shutdown.889
-rw-r--r--man/sysvinit.8155
-rw-r--r--misc/BIN48
-rw-r--r--misc/ETC18
-rw-r--r--misc/HOME25
-rw-r--r--misc/MAN19
-rw-r--r--misc/child_block.c9
-rw-r--r--misc/dup2_inout.c13
-rw-r--r--misc/fmt_argv.c10
-rw-r--r--misc/initreq.c31
-rw-r--r--misc/opendevconsole.c12
-rw-r--r--misc/set_sigaction.c10
-rw-r--r--misc/sulogin.c9
-rw-r--r--misc/system_child_block.c9
-rw-r--r--misc/system_set_sigaction.c42
-rw-r--r--misc/try_helper.h29
-rw-r--r--misc/try_int.h25
-rw-r--r--misc/trypagesize.c5
-rw-r--r--misc/tryprocess.c132
-rw-r--r--misc/tryulong32.c6
-rw-r--r--mmap_alloca.h51
-rw-r--r--ninit.c345
-rw-r--r--ninit.h82
-rw-r--r--ninit.spec60
-rw-r--r--ninitfeatures.h199
-rw-r--r--nkillall.c179
-rw-r--r--nsvc.c415
-rw-r--r--nsvc_buffer.h19
-rw-r--r--nsvc_help23
-rw-r--r--open_inout.h43
-rw-r--r--ops345
-rw-r--r--pidfile.c65
-rw-r--r--printf.c99
-rw-r--r--procfs.c84
-rw-r--r--pututmpid.c142
-rw-r--r--reboot.c38
-rw-r--r--reload.c321
-rw-r--r--remove.c74
-rw-r--r--run.c177
-rw-r--r--runlevel.c137
-rwxr-xr-xscripts/conf18
-rwxr-xr-xscripts/scan34
-rwxr-xr-xscripts/service104
-rwxr-xr-xscripts/tests.sh126
-rwxr-xr-xscripts/update.sh27
-rw-r--r--shutdown.c303
-rw-r--r--sighandler.h26
-rw-r--r--softlimit.c116
-rw-r--r--struct_root.h33
-rw-r--r--system/Files55
-rw-r--r--system/README5
-rw-r--r--system/__alarm.c17
-rw-r--r--system/__environ.c1
-rw-r--r--system/__errno.c1
-rw-r--r--system/__errno_location.c3
-rw-r--r--system/__nice.c8
-rw-r--r--system/__sbrk.c25
-rw-r--r--system/__time.c14
-rw-r--r--system/__waitpid.c11
-rw-r--r--system/features.h7
-rw-r--r--system/i386/Flags1
-rw-r--r--system/i386/Makefile5
-rw-r--r--system/i386/__restore.S12
-rw-r--r--system/i386/__restore_rt.S9
-rw-r--r--system/i386/mmap.S11
-rw-r--r--system/i386/start.S37
-rw-r--r--system/i386/syscalls.h34
-rw-r--r--system/i386/unified.S68
-rw-r--r--system/i386/unified_256.S7
-rw-r--r--system/x86_64/Flags1
-rw-r--r--system/x86_64/Makefile5
-rw-r--r--system/x86_64/__restore_rt.S8
-rw-r--r--system/x86_64/gettimeofday.S18
-rw-r--r--system/x86_64/start.S17
-rw-r--r--system/x86_64/syscalls.h20
-rw-r--r--system/x86_64/unified.S27
-rw-r--r--system/x86_64/waitpid.S10
-rw-r--r--sysvinit.c143
-rw-r--r--t_write.h19
-rw-r--r--tryservice.h16
-rw-r--r--tryservice_nsvc.h34
-rw-r--r--uid.h26
-rw-r--r--update.c96
-rw-r--r--wait.c96
-rw-r--r--wait_services.h37
183 files changed, 9899 insertions, 0 deletions
diff --git a/00THANKS b/00THANKS
new file mode 100644
index 0000000..9e33b28
--- /dev/null
+++ b/00THANKS
@@ -0,0 +1,6 @@
+D. J. Bernstein: daemontools author
+Felix von Leitner: dietlibc, minit author
+Jan Skakula: port to archlinux; making services
+Pencho Marinov: always test the new versions
+Ilian Kostadinov: many discusion obout ninit features
+Boris Grozev: updates all manpages
diff --git a/CHANGES b/CHANGES
new file mode 100644
index 0000000..f19e9d8
--- /dev/null
+++ b/CHANGES
@@ -0,0 +1,129 @@
+0.14: 2010-01-19 00:27:01
+ added nkillall.8
+ gid is not default anymore. see ninitfeatures.h
+ uid supports 22:33:100:200:300
+ nkillall: \e escape
+ shutdown: set cron off
+ nkillall: signals with letters like nsvc
+ shutdown and nkillall: makes more sync(2), flag -q
+ added THANKS ;-)
+ added helpers: remove procfs; thanks to them snvc is smaller
+ nsvc options -onumber -unumber; option -E
+ Cron has option -C
+ /etc/ninit/.nsvc_help
+ nsvc, shutdown, reload uses variables NINIT_MEMORY and NINIT_HOME
+ nsvc: option -S
+ shutdown: option -s
+ manpages for all programs in /sbin ;-)
+ update manpages (Boris Grozev)
+ Makefile: man_txt
+ serdo.c: read only one file again
+ better sleep in open_inout.h
+ serdo.c improvments; buildin: killall5, simple echo
+ nsvc.c: -C+number
+ scripts/conf
+ ninit.8: NONROOT USAGE, using /sbin/init with ninit
+ ninit-scan: (Wayne Marshall)
+
+0.13.7: 2009-12-16 10:47:50
+ bootlog.c (flush_root)
+ 0.13.1.4 renamed to 0.13.2
+ service start 'end' (if it exixts; X_OK) after finishing
+ fixed a bug in shutdown.c (thx to Jan)
+ With flag -s ninit-shutdown don't start halt and reboot services
+ shutdown.c: improvements; flags -S -q
+ shutdown.c: removed flags -s -q; new flags -E -v -T
+ added nkillall
+ shutdown.c: options -m and -E work together
+ nkillall print escape like echo
+
+0.13.1: 2009-06-27 12:59:38
+ updated bootlog
+ Makefile logs 'make tests' in tests_log using boolog
+ fixing quotes in serdo
+
+0.13: 2009-01-10 18:11:42
+ it's possible to set parameters in wait. example: some_service:180:3
+ install-bin print help and string errors
+ better sync mode. maxsync is removed. echo 200 > sync
+ updated scripts/ninit_test.sh
+ comments (#) in depends, params, environ, wait, softlimit
+ install-bin (verbose mode)
+ nsvc -V
+ errmsg_put.c; buffer_*.c
+ setup, rsetup, sys-rsetup starts with args: $1=service $2=service_pid
+ EXTRACT_* trick (automatic create header files)
+ stuct process is 20 bytes on x86_64 (see struct_root.h)
+ changed BIFFER_INIT macro
+ utmp_io.c
+ Makefile: install_other
+ serdo is intslled only with: make install_other
+ t_write.h uses PIPE_BUF (thx Laurent Bercot)
+ nsvc.8 improvements
+ ninit_test.sh creates services in ./etc/ninit/
+ ninit-huge
+ added printf.c and ninit.spec
+ make FLAG_DEBUG=no
+ removed flag X_OK in some syscalls access.
+ better addprocess
+
+0.12.1: 2008-01-03 17:15:08
+ ninit-depends. convert directory to file (depends.dir -> depends)
+ removed unused stat.h in headers
+ updated serdo (static)
+ two environ vars: NINIT_HOME, INIT_HOME. (Thanks to Stamatis Mitrofanis)
+ ninit logo: /etc/ninit/.sync
+ install-bin uses chown32 instead chown on some systems
+ err.c and err_b.c uses the macros va_start and va_arg
+ fu.c (u &= 0xffffffff)
+
+0.12:
+ almost all arrays are moved in stack. small data and bss sections.
+ assembler functions (i386) for some DJB functions.
+ modifications in Makefile.
+ 100 Euro security guarantee.
+
+0.11.2:
+ ninit-reload accept agument -e (change environ) -E file_env
+ program ~/sys/update
+ service flags: pause, pause-wait
+ service flag cron: a:b[:c]
+ can contain many lines.
+ flag -K for nsvc.
+ pidfilehack --> ninit-pidfile.
+ make i386 -- build static daemons (don't use dietlibc)
+ Makefile is smaller and simpler
+ gcc-4.1.2 works now.
+ install-bin
+ stuct mem in initialized in the main program.
+
+0.11.1:
+ ninit-reload accepts args (-r3 -r5 -r33 ...)
+ ninit-inittab sets the variables NINIT_RUNLEVEL, INIT_VERSION
+ if /etc/minit/{in|out} FIFO's exist then make symbolic links.
+ new run flags: sys-rsetup, pidfile
+ if the service sysvinit exist, ninit create /dev/initctl
+ ninit-reload and nsvc checks for environ NINIT_HOME
+ nsvc -[drR] ALL - change all services.
+ Change service ALL with: nsvc -[drR] ops ALL
+ new program: ninit-sysvinit. configuration file: sysvinit-timeout
+ fimeout[:fork-mode]. example: echo 600:0 > sysvinit-timeout
+ ninit catch signal SIGPWR
+ new run flags: alarm, maxsync
+ nsvc output is different
+ shell script ninit-service
+ new program run-wait
+
+0.11:
+ Sun Jun 17 22:08:22 EEST 2007
+ I decided to rename all without changes.
+ The name ninit must be spelled nano-init.
+
+0.9.11:
+ rewritten by Nikola Vladov
+
+0.9.1:
+ fix embarassing typo in msvc (Gelu G. Lupas)
+
+0.9:
+ See minit CHANGES - http://www.fefe.de/minit
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..5b6e7c6
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..c0e4ea4
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,221 @@
+SHELL = /bin/sh
+VPATH = contrib:djb:lib:misc
+DESTDIR:=/
+
+DIET=
+CC=gcc $(GCC_FLAGS)
+CFLAGS=-pipe -Os -Wall -W
+LDFLAGS=-s
+FLAG_DEBUG = no
+
+MYARCH:=$(shell uname -m | sed -e 's/i[4-9]86/i386/' -e 's/armv[3-6]t\?e\?[lb]/arm/')
+MAN_PAGES:=$(shell test -d /usr/share/man && echo usr/share/man || echo usr/man)
+
+ifeq ($(MYARCH),i386)
+include system/$(MYARCH)/Flags
+endif
+ifeq ($(MYARCH),x86_64)
+include system/$(MYARCH)/Flags
+endif
+
+# If the assembler fail on your host change i386 bellow to XXXX
+ifeq ($(MYARCH),i386)
+ALL_LIB = $(shell sed -e 's/Z./S/' -e 's/^[a-z]*\///' library_files)
+else
+ALL_LIB = $(shell sed -e 's/Z.//' -e 's/^[a-z]*\///' library_files)
+endif
+
+ifdef DIET
+CFLAGS += -nostdinc
+else
+CFLAGS += $(OPTIMIZATION)
+endif
+
+N_FLAGS = -nostdlib -DINIT_SYSTEM system/$(MYARCH)/start.o
+N_FLAGS += $(CFLAGS) $(LDFLAGS)
+N_LIB = ninit.a system/$(MYARCH)/system.a
+
+ifeq ($(FLAG_DEBUG),no)
+CCC_ = @echo ' CC $< ' ;
+CCL_ = @echo ' CL $< -> $@ ' ;
+C = @
+else
+CCC_ =
+CCL_ =
+C =
+endif
+
+CC_C = $(DIET) $(CC) $(CFLAGS)
+CC_L = $(DIET) $(CC) $(CFLAGS) $(LDFLAGS)
+
+CCC = $(CCC_) $(CC_C)
+CCL = $(CCL_) $(CC_L)
+STR = strip -R .comment -R .note
+
+ALL = ninit run wait update nsvc reload pidfile reboot \
+ runlevel sysvinit inittab shutdown pututmpid bootlog install-bin \
+ env sleeprun conditional-init serdo argv0 ninit-mmap ninit-huge nkillall \
+ remove procfs
+ALL_MAN = ninit.8.gz nsvc.8.gz pututmpid.8.gz shutdown.8.gz nkillall.8.gz \
+ runlevel.8.gz sysvinit.8.gz pidfile.8.gz reboot.8.gz reload.8.gz \
+ bootlog.8.gz service.8.gz inittab.8.gz scan.8.gz
+
+BYTE_C = $(wildcard djb/byte_*.c)
+BUFFER_C = $(wildcard djb/buffer_*.c lib/err_b.c)
+
+all: $(ALL) $(ALL_MAN) Version start_tests
+ninit.a: $(ALL_LIB)
+ ar cr $*.a $^
+
+include_h_files = all_defs.h buffer_defs.h byte_defs.h utmp_defs.h \
+ addprocess.h open_inout.h tryservice_nsvc.h \
+ findservice.h mmap_alloca.h sighandler.h wait_services.h get_services.h \
+ ninit.h t_write.h initreq.h ninitfeatures.h tryservice.h error_table.h \
+ struct_root.h uid.h
+help_files = $(include_h_files) int_defs.h pagesize_defs.h process_defs.h \
+ninit.a
+
+%.o: %.c $(include_h_files) process_defs.h
+ $(CCC) -c -o $@ $<
+S%.o: S/%.S $(include_h_files)
+ $(CCC) -c -o $@ $<
+
+printf: printf.c
+ $(CCL) -o $@ $<
+ $(C) $(STR) $@
+bin-$(MYARCH)/%: %.c $(help_files)
+ $(CCL_) $(CC) $(N_FLAGS) $(TINY_FLAGS) -o $@ $< $(N_LIB)
+%: %.c $(help_files)
+ $(CCL) -o $@ $< ninit.a
+
+ninit-mmap.c: ninit.c printf
+ $(C) ./printf '%s\n%s\n' '#define INIT_MMAP' '#include "ninit.c"' > $@
+ninit-huge.c: ninit.c printf
+ $(C) ./printf '%s\n%s\n' '#define INIT_TIMEOUT_WRITE' '#include "ninit.c"' > $@
+buffer_defs.h: $(BUFFER_C)
+ $(C) ./get_headers $@ $^ > $@
+byte_defs.h: $(BYTE_C) djb/str_len.c
+ $(C) ./get_headers $@ $^ > $@
+utmp_defs.h: lib/do_wtmp.c lib/utmp_io.c
+ $(C) ./get_headers $@ $^ > $@
+all_defs.h: library_files printf
+ $(C) ./get_headers $@ -Lint_defs.h `sed -e 's/\.o/\.c/' -e 's/Z/djb/' library_files` | sed -e '/struct.*utmp/d' -e '/buffer/d' > $@
+ $(C) ./printf '\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n\n' \
+ "ARCH = $(MYARCH)" "`uname -a || true`" "DIET = $(DIET)" "CC = $(CC)" \
+ "CFLAGS = $(CFLAGS)" "LDFLAGS = $(LDFLAGS)" "FLAG_DEBUG = $(FLAG_DEBUG)" \
+
+process_defs.h: misc/tryprocess.c all_defs.h
+ $(C) rm -f $@Z; $(CC_L) -o $@Z $<
+ ./$@Z > $@
+ $(C) sed -ne /_MASK_LEN/p -e /_SIZE/p -e /_NAME_/p -e /_CODED/p $@
+ $(C) rm -f $@Z
+int_defs.h: misc/tryulong32.c misc/try_int.h2
+ $(C) ( ( rm -f $@Z; $(CC_L) -o $@Z $< && ./$@Z ) >/dev/null 2>&1 \
+ && sed -e 's/Z/long/' misc/try_int.h2 || \
+ sed -e 's/Z/int/' misc/try_int.h2 ) > $@
+ $(C) sed -n -e '/typedef/p' $@; rm -f $@Z
+pagesize_defs.h: misc/trypagesize.c
+ $(C) ( ( rm -f $@Z; $(CC_L) -o $@Z $< && ./$@Z ) >/dev/null 2>&1 \
+ && echo '#define page_size 4096' || echo '#define page_size 8192' ) > $@
+ $(C) cat $@; rm -f $@Z
+initreq: misc/initreq.c
+ $(CCL) -o $@ $<
+
+withdiet:
+ make DIET='diet -Os'
+nodiet:
+ make DIET=
+
+B=bin-$(MYARCH)
+STATIC_FILES=$(shell echo '' $(ALL) | sed -e "s/ */ $(B)\//g")
+$(MYARCH):
+ test -d bin-$(MYARCH) || mkdir bin-$(MYARCH)
+ make DIET=
+ cd system/$(MYARCH) && make FLAG_DEBUG=$(FLAG_DEBUG)
+ make system-$(MYARCH) DIET= LDFLAGS="$(TINY_LDFLAGS) $(LDFLAGS)"
+$(MYARCH)-tiny: printf
+ $(C) ./printf "\n%s\n\t%s\n\n" \
+ 'exec the following line:' \
+ 'make clean $(MYARCH) TINY_LDFLAGS=-Wl,-N'
+system-$(MYARCH): $(STATIC_FILES) printf
+ $(C) ./printf "\n\t%s\n\t%s\n\n" \
+ 'You can install ./'$(B)'/* static-files with:' \
+ 'cp ./'$(B)'/* . ; make install'
+
+clean:
+ rm -f $(ALL) \
+ system/$(MYARCH)/*.o system/$(MYARCH)/*.a system/$(MYARCH)/system_*.S \
+ *.o *.a a.out contrib/*.o djb/*.o djb/*.s S/*.o misc/*.o misc/*.s \
+ lib/*.o lib/*.s *.8.gz *_defs.h *_defs.hZ x y z initreq printf \
+ ninit_server.sh services.sh after-reload ninit.data \
+ ninit-mmap.c ninit-huge.c Version tests_log* OTHER
+ rm -rf home etc bin-* mantxt
+
+dietlibc/bin-$(MYARCH)/diet:
+ cvs -d :pserver:[email protected]:/cvs -z9 co dietlibc
+ cd dietlibc && make
+dietbuild: dietlibc/bin-$(MYARCH)/diet
+ DIETHOME=$(CURDIR)/dietlibc make DIET="$(CURDIR)/$< -Os"
+%.gz: man/%
+ $(C) gzip -9c $< > $@
+mantxt/%: man/%.8
+ $(C) test -d mantxt || mkdir mantxt
+ man ./$< | col -b > [email protected]
+man_txt: $(patsubst %.8.gz,mantxt/%,$(ALL_MAN))
+
+tests: scripts/tests.sh $(ALL) Version
+ ./bootlog -ctr 200000 tests_log ./$<
+ @sleep 1; ./printf 'Above output is saved in file: \e[1;34mtests_log\e[0;39m.\r\nRead it using the programs cat/more/less.\r\n'
+strip: $(patsubst printf,,$(ALL))
+ $(STR) $^ bin-$(MYARCH)/* || true
+
+D=$(DESTDIR)
+install: $(ALL) $(ALL_MAN) scripts/update.sh Version printf
+ ./install-bin $(D) < misc/BIN
+ ./install-bin $(D)/$(MAN_PAGES) < misc/MAN
+ ./scripts/update.sh $(D)
+ $(C) ./printf "\n\t%s\n\n" \
+ 'Install some additional programs with: make install_other'
+
+install_other: $(ALL)
+ sed -e 's/# //' -e /600/q misc/BIN > OTHER
+ ./install-bin $(D) < OTHER
+
+start_tests: $(ALL) $(ALL_MAN) Version printf
+ $(C) ./printf "\n\tStart now: make tests\n\n"
+ser_vi_ces: inittab
+ ./inittab /etc/inittab /etc/ninit services.sh
+ ./services.sh
+package: distro
+distro:
+ umask 022 && mkdir -p /tmp/ninit.distro/$(MAN_PAGES)
+ make install DESTDIR=/tmp/ninit.distro
+ cd /tmp/ninit.distro && tar -cjf \
+ /tmp/$(VERSION)-$(MYARCH).tar.bz2 --owner=root --group=root *
+
+VERSION=ninit-$(shell head -n 1 CHANGES|sed 's/:.*//')
+VERSION_LONG=$(shell head -n 1 CHANGES)
+TIMENOW=$(shell date -u "+%Y-%m-%d %H:%M:%S")
+CURNAME=$(notdir $(CURDIR))
+RRR=--owner=root --group=root
+Version: CHANGES printf
+ ./printf '\r\n\e[1;34m%s\e[0;39m:%s\r\n%s\e[1;35m%s\e[0;39m\r\n' \
+ 'NINIT' ' Version: $(VERSION_LONG)' \
+ 'source: ' 'http://riemann.fmi.uni-sofia.bg/ninit/' > Version
+
+tests.tar:
+ tar -cv $(RRR) home/default/* home/env/environ home/env/run \
+ home/sh/* home/S/* home/sleep/* | gzip -9 > home.tar.gz
+rename:
+ if test $(CURNAME) != $(VERSION); then cd .. && mv $(CURNAME) $(VERSION); fi
+TAR=tar
+TAR_OPT=
+tar: rename
+ sed -e "1s/^\(.*: \)\(.*\)/\1$(TIMENOW)/" CHANGES > CHANGES.tmp
+ mv CHANGES.tmp CHANGES
+ cd .. && $(TAR) cvjf $(VERSION).tar.bz2 $(TAR_OPT) $(RRR) \
+ --exclude $(VERSION)/dietlibc $(VERSION)
+packit:
+ make clean tar TAR=tar.f TAR_OPT=--sort
+rpm: ninit.spec
+ rpmbuild -ba --clean $<
diff --git a/README b/README
new file mode 100644
index 0000000..b48ee4b
--- /dev/null
+++ b/README
@@ -0,0 +1,213 @@
+See also: http://riemann.fmi.uni-sofia.bg/ninit/
+
+Each service gets its own directory under /etc/ninit (change this in the
+source, it's a #define right at the start of ninitfeatures.h).
+
+
+Each service directory can contain the following files/symlinks:
+
+ depends
+
+ a plain text file containing a service name per line.
+ Example: /etc/ninit/sshd/depends could contain "network".
+ Each of these services will be started before this service is
+ started. If you need to wait for static initializations to
+ complete, use the sync flag. See also the wait flag below.
+
+ run
+
+ a symbolic link to the program name. No hard link, because argv[0]
+ for the programs is created by extracting the part after the last
+ slash in the contents of the symbolic link.
+ Example: "/usr/bin/sshd" would be run with argv[0]="sshd".
+
+ end
+
+ similar to run. After the service finish "end" will be executed.
+ The helper does not set environ, softlimit, uid/gid restrictions.
+ You can put there for example:
+ #!/bin/sh
+ exec /bin/nsvc -o service_name
+ Use this option instead of sync or wait mode. Let for example
+ services B,C,D must be started after A finish. There are two
+ possible solutions:
+ Bad: start A in sync mode
+ Good: start A i normal mode and put in "end"
+ #!/bin/sh
+ exec /bin/nsvc -o B C D
+
+ params
+
+ a plain text file containing command line parameters for the
+ service, one parameter per line. No shell expansion is done. If
+ you need shell expansion, have run point to a shell script instead
+ of the real daemon. Note: Have the shell script exec the daemon
+ instead of simply running it to save system resources.
+
+ environ
+
+ similar to params. run appends contents of this file to the environ.
+ If the first line has zero length, unset all environ.
+ If a line does not have '=' (HOME), unset this variable.
+
+ wait
+
+ similar to params. /etc/inint/run waits for services included in
+ this file to finish and after that start a current service.
+ Include wait services also in depends. This is different from sync.
+ Let a service B must be started after a service A finish.
+ One can sync A. This will block all other services.
+ In this case it's better to use wait instead of sync.
+ It's possible to set wait parameters (see maxwait) at the end of
+ each entry. Example: some_service:180:3
+
+ maxwait
+
+ a plain text file containing the values n1:n2. Wait no more
+ than n1 sec a service for finish. See wait. Default is 600 sec.
+ The value n1=0 means -- wait until the service finish.
+
+
+ softlimit
+
+ see DJB program softlimit. Similar to params. Lines
+ with the same options as softlimit (skip leading '-'). Example:
+ echo m300000 > softlimit; echo o30 >> softlimit
+
+ respawn
+
+ touch this file to make ninit respawn the process when it dies.
+ This should be touched for getty and network servers.
+
+ sync
+
+ touch this file to make ninit wait until the service ends. sync is
+ mutually exclusive with respawn. This is meant for static
+ initializations like "ifconfig". See also wait.
+ If sync contains a nonzero number n ninit will wait max n sec
+ the service to finish. If it does not ends until n secs then
+ ninit continues to work. Example: echo 600 > sync
+
+ log
+
+ if this directory exists, it is taken as service and ninit creates
+ a named pipe log/in and soft link of log/in to out. If the log
+ service can not be started, this service will block if it writes
+ to stdout.
+
+ nice
+
+ a plain text file containing the value to add to the nice level
+ via the nice system call.
+
+ sleep
+
+ a plain text file containing the value n. sleep n secs after fork
+ before running child.
+
+ in
+
+ this file(named pipe) is used for stdin.
+
+ out
+
+ this file(named pipe) is used for stdout and stderr.
+ e.g. a symlink to /dev/null
+
+ gid
+
+ a plain text file containing a number.
+ Exec the program with this GID. Example echo -n 234 > gid
+ To activate this feature edit the file ninitfeatures.h
+ and install the package again.
+
+ uid
+
+ exec the program with this UID. It can contain also UID:GID
+ It is possible to write here also the supplementary groups.
+ For example echo 23:99:240:320:100 > uid start the service with:
+ UID=23, GID=99, GROUPS=99,240,320,100.
+
+ setup
+
+ if this file exist it is started just before run in sync mode.
+ If uid/gid or softlimit exist they are applied first and
+ after that setup is executed.
+
+ rsetup
+
+ similar to setup. It is executed before setup as root and
+ not softlimit, uid/gid, in/out restrictions. If you
+ want to create/modify params, wait, depends... see sys-rsetup flag.
+
+ sys-rsetup
+
+ similar to setup. It is executed first as root.
+ It can be used to create/modify params, wait, depends...
+
+ pidfile
+
+ easy method to setup services which run in background.
+ Prepare them as ordinary services and write the name of the pidfile.
+ Don't use link here!
+ Example: echo -n /var/run/gpm.pid > pidfile
+
+ alarm
+
+ a plain text file containing a number n.
+ Do alarm(n) before starting the child.
+
+ pause
+
+ if this file exist run selfkills with SIGSTOP signal.
+
+ pause-wait
+
+ if this file exist run-wait selfkills with SIGSTOP signal.
+
+ cron
+
+ a plain text file containing lines with numbers a:b[:c]
+ for each line run wait find a solution of
+ cron = 60 * (a*x+b) > now (x is integer)
+ The smallest cron is next cron-start for this service.
+ The number a=10080 (24*60*7) is special. For example 10080:25
+ means: Sunday 00:25:00 UTC
+
+ninit will try to run the command line arguments as services. The
+kernel passes its arguments to init. That means you can for example
+have a service /etc/ninit/sos-shell that starts a /bin/sh and then use
+LILO to boot "bzImage sos-shell". ninit will then run that service.
+If none of the services worked (or none were given), ninit will spawn
+the service "default". The normal way to configure ninit is to have
+default be an empty service and just list all the services you want
+ninit to start at boot time in default/depends.
+
+Other special services (besides "default") are "ctrlaltdel" and "kbreq".
+ctrlaltdel will be run when the console user presses ctrl-alt-del and is
+meant to reboot the computer. kbreq is the keyboard request, which
+can be mapped using loadkeys. On my box, it is on Alt+Arrow-Up. I use
+it to shut down the computer.
+
+--------------------------
+Memory allocation in NINIT. ninit uses only alloca for RAM.
+
+One service = 21 + strlen(service_name). Example getty/1 is 28 bytes.
+If you need to load more than 50 services then use at boot process
+something like:
+ /sbin/ninit -M3200
+
+after reload/reboot ninit will work with 3200 bytes buffer.
+default buffer is 1536. Good for 50-60 services.
+If someone uses more than 50 services let me know.
+
+--------------------------
+After building a new ninit test/install it with:
+ ninit-reload -v -u /path/to/new/ninit [-M2000]
+ cp /path/to/new/ninit /sbin/ninit
+ ninit-reload -v -u /sbin/ninit [argv1 argv2 ...]
+some of argvs can be -M2000 or -M3200 ...
+
+There is no need of reboot! Dump memory buffer with:
+ ninit-reload -m > /tmp/ninit.mem
+ less /tmp/ninit.mem
diff --git a/S/atoulong.S b/S/atoulong.S
new file mode 100644
index 0000000..7d141ed
--- /dev/null
+++ b/S/atoulong.S
@@ -0,0 +1,17 @@
+.globl atoulong
+.type atoulong,@function
+atoulong:
+ xorl %edx,%edx
+ xorl %eax,%eax
+ movl 4(%esp),%ecx
+ jmp .L1
+.L2:
+ imull $10,%eax,%eax
+ addl %edx,%eax
+ incl %ecx
+.L1:
+ movb (%ecx),%dl
+ subb $48,%dl
+ cmpb $9,%dl
+ jbe .L2
+ ret
diff --git a/S/byte_copy.S b/S/byte_copy.S
new file mode 100644
index 0000000..44ff449
--- /dev/null
+++ b/S/byte_copy.S
@@ -0,0 +1,23 @@
+.globl byte_copy
+.type byte_copy, @function
+byte_copy:
+ pushl %edi
+ pushl %esi
+ movl 12(%esp), %edi
+ movl 16(%esp), %ecx
+ movl 20(%esp), %esi
+ cld
+
+#ifdef BYTE_COPY_FAST
+ movl %ecx, %eax
+ shrl $2, %ecx
+ andl $3, %eax
+
+ rep movsl
+ movl %eax, %ecx
+#endif
+ rep movsb
+
+ popl %esi
+ popl %edi
+ ret
diff --git a/S/byte_copyr.S b/S/byte_copyr.S
new file mode 100644
index 0000000..bcc9b21
--- /dev/null
+++ b/S/byte_copyr.S
@@ -0,0 +1,22 @@
+.text
+.globl byte_copyr
+.type byte_copyr, @function
+byte_copyr:
+ pushl %edi
+ pushl %esi
+ movl 12(%esp), %edi
+ movl 20(%esp), %esi
+ movl 16(%esp), %ecx
+
+ decl %edi
+ decl %esi
+ addl %ecx, %edi
+ addl %ecx, %esi
+
+ std
+ rep movsb
+ cld
+
+ popl %esi
+ popl %edi
+ ret
diff --git a/S/byte_diff.S b/S/byte_diff.S
new file mode 100644
index 0000000..f9d6c16
--- /dev/null
+++ b/S/byte_diff.S
@@ -0,0 +1,19 @@
+.global byte_diff
+.type byte_diff,function
+byte_diff:
+ pushl %esi
+ pushl %edi
+ xorl %eax, %eax
+ movl 12(%esp), %esi
+ movl 16(%esp), %ecx
+ movl 20(%esp), %edi
+
+ cld
+ rep cmpsb
+ jz .Lout
+ sbbl %eax, %eax
+ orl $1, %eax
+.Lout:
+ popl %edi
+ popl %esi
+ ret
diff --git a/S/byte_set.S b/S/byte_set.S
new file mode 100644
index 0000000..a61b3ad
--- /dev/null
+++ b/S/byte_set.S
@@ -0,0 +1,5 @@
+.globl byte_set
+.type byte_set, @function
+byte_set:
+ movb 12(%esp), %al
+ jmp byte_zero_end
diff --git a/S/byte_zero.S b/S/byte_zero.S
new file mode 100644
index 0000000..01c166b
--- /dev/null
+++ b/S/byte_zero.S
@@ -0,0 +1,16 @@
+.globl byte_zero
+.type byte_zero, @function
+byte_zero:
+ xorb %al, %al
+
+.globl byte_zero_end
+byte_zero_end:
+ pushl %edi
+ movl 8(%esp), %edi
+ movl 12(%esp), %ecx
+
+ cld
+ rep stosb
+
+ popl %edi
+ ret
diff --git a/S/str_chr.S b/S/str_chr.S
new file mode 100644
index 0000000..5fec8b2
--- /dev/null
+++ b/S/str_chr.S
@@ -0,0 +1,17 @@
+.globl str_chr
+.type str_chr, @function
+str_chr:
+ pushl %esi
+ movl 8(%esp), %esi
+ movb 12(%esp), %ah
+.L1:
+ lodsb
+ cmpb %ah,%al
+ je .L2
+ testb %al,%al
+ jne .L1
+.L2:
+ leal -1(%esi), %eax
+ subl 8(%esp), %eax
+ popl %esi
+ ret
diff --git a/S/str_copy.S b/S/str_copy.S
new file mode 100644
index 0000000..c24dbac
--- /dev/null
+++ b/S/str_copy.S
@@ -0,0 +1,18 @@
+.globl str_copy
+.type str_copy, @function
+str_copy:
+ pushl %edi
+ pushl %esi
+ movl 12(%esp), %edi
+ movl 16(%esp), %esi
+.L1:
+ lodsb
+ stosb
+ testb %al,%al
+ jne .L1
+
+ leal -1(%edi), %eax
+ subl 12(%esp), %eax
+ popl %esi
+ popl %edi
+ ret
diff --git a/S/str_copyn.S b/S/str_copyn.S
new file mode 100644
index 0000000..7a8765c
--- /dev/null
+++ b/S/str_copyn.S
@@ -0,0 +1,22 @@
+.globl str_copyn
+.type str_copyn, @function
+str_copyn:
+ pushl %edi
+ pushl %esi
+ movl 12(%esp), %edi
+ movl 16(%esp), %esi
+ movl 20(%esp), %ecx
+.L1:
+ decl %ecx
+ js .L2
+ lodsb
+ stosb
+ testb %al,%al
+ jne .L1
+ decl %edi
+.L2:
+ movl %edi, %eax
+ subl 12(%esp), %eax
+ popl %esi
+ popl %edi
+ ret
diff --git a/S/str_diff.S b/S/str_diff.S
new file mode 100644
index 0000000..a90724e
--- /dev/null
+++ b/S/str_diff.S
@@ -0,0 +1,22 @@
+.globl str_diff
+.type str_diff, @function
+str_diff:
+ pushl %edi
+ pushl %esi
+ movl 12(%esp), %esi
+ movl 16(%esp), %edi
+.L1:
+ lodsb
+ scasb
+ jne .L2
+ testb %al,%al
+ jne .L1
+ xorl %eax,%eax
+ jmp .L3
+.L2:
+ sbbl %eax,%eax
+ orb $1,%al
+.L3:
+ popl %esi
+ popl %edi
+ ret
diff --git a/S/str_diffn.S b/S/str_diffn.S
new file mode 100644
index 0000000..954d88f
--- /dev/null
+++ b/S/str_diffn.S
@@ -0,0 +1,26 @@
+.globl str_diffn
+.type str_diffn, @function
+str_diffn:
+ pushl %edi
+ pushl %esi
+ movl 12(%esp), %esi
+ movl 16(%esp), %edi
+ movl 20(%esp), %ecx
+.L1:
+ decl %ecx
+ js .L2
+ lodsb
+ scasb
+ jne .L3
+ testb %al,%al
+ jne .L1
+.L2:
+ xorl %eax,%eax
+ jmp .L4
+.L3:
+ sbbl %eax,%eax
+ orb $1,%al
+.L4:
+ popl %esi
+ popl %edi
+ ret
diff --git a/S/str_len.S b/S/str_len.S
new file mode 100644
index 0000000..ba3fa7e
--- /dev/null
+++ b/S/str_len.S
@@ -0,0 +1,11 @@
+.globl str_len
+.type str_len, @function
+str_len:
+ movl 4(%esp), %ecx
+ orl $-1, %eax
+.L1:
+ incl %eax
+ cmpb $0, (%ecx, %eax)
+ jne .L1
+
+ ret
diff --git a/S/str_rchr.S b/S/str_rchr.S
new file mode 100644
index 0000000..032c393
--- /dev/null
+++ b/S/str_rchr.S
@@ -0,0 +1,24 @@
+.globl str_rchr
+.type str_rchr, @function
+str_rchr:
+ pushl %esi
+ movl 8(%esp), %esi
+ movb 12(%esp), %ah
+ xorl %ecx, %ecx
+.L1:
+ lodsb
+ cmpb %ah,%al
+ jne .L0
+ movl %esi, %ecx
+.L0:
+ testb %al,%al
+ jne .L1
+
+ testl %ecx, %ecx
+ jnz .L3
+ movl %esi, %ecx
+.L3:
+ subl 8(%esp), %ecx
+ leal -1(%ecx), %eax
+ popl %esi
+ ret
diff --git a/addprocess.h b/addprocess.h
new file mode 100644
index 0000000..e4c04cb
--- /dev/null
+++ b/addprocess.h
@@ -0,0 +1,30 @@
+/* 0 <= mem.l <= mem.r <= mem.a */
+
+/* add process to data structure, return index or -1 */
+static int addprocess(void *p, char *service) {
+ unsigned int root_len, serv_len = str_len(service) + 1;
+ void *name;
+ struct process *pr;
+
+ root_len = mem.l + PROCESS_SIZE;
+ if (root_len + serv_len + 32 > mem.r)
+ { write(1,"init: out of memory\n",20); return -1; }
+
+ mem.r -= serv_len;
+ ++maxprocess;
+
+ pr = root;
+ root = mem.x + (mem.r & ~15) - root_len;
+ byte_copy(root, mem.l, pr);
+
+ pr = root + maxprocess;
+ byte_copy(pr, PROCESS_SIZE, p);
+
+ mem.l += PROCESS_SIZE;
+ name = mem.x + mem.r;
+ byte_copy(name, serv_len, service);
+
+ pr->name = process_name_set;
+ next_cron = 0;
+ return maxprocess;
+}
diff --git a/check_opt.h b/check_opt.h
new file mode 100644
index 0000000..610edd8
--- /dev/null
+++ b/check_opt.h
@@ -0,0 +1,8 @@
+static int check_opt(char **argv, char **opt) {
+ if (argv[0][2]) { *opt = argv[0]+2; return 0; }
+ else if (argv[1]) { *opt = argv[1]; return 1; }
+ carp("Option ",argv[0]," requires an argument");
+ _exit(1);
+}
+
+#define chk_opt(argv,opt) argv+=check_opt(argv,&opt)
diff --git a/contrib/argv0.c b/contrib/argv0.c
new file mode 100644
index 0000000..e2eeb81
--- /dev/null
+++ b/contrib/argv0.c
@@ -0,0 +1,13 @@
+#include "../ninitfeatures.h"
+#include "../error_table.h"
+
+int main(int argc,char **argv,char **envp) {
+ errmsg_iam("argv0");
+ if (argc < 3) {
+ carp("usage: argv0", " realname program [ arg ... ]");
+ return 100;
+ }
+ pathexec_run(argv[1],argv + 2,envp);
+ carp("unable to run ",argv[1],": ",error_string(table,errno));
+ return 111;
+}
diff --git a/contrib/bootlog.c b/contrib/bootlog.c
new file mode 120000
index 0000000..f2f9083
--- /dev/null
+++ b/contrib/bootlog.c
@@ -0,0 +1 @@
+bootlog_sbrk.c \ No newline at end of file
diff --git a/contrib/bootlog.h b/contrib/bootlog.h
new file mode 100644
index 0000000..967ed6d
--- /dev/null
+++ b/contrib/bootlog.h
@@ -0,0 +1,100 @@
+/* return -1 on error */
+static int xx_write(int fd, void *buf, size_t len) {
+ char *x = buf;
+ ssize_t w;
+ while (len) {
+ w = write(fd, buf, len);
+ if (w <= 0) {
+ if (w < 0 && errno == EINTR) continue;
+ return -1;
+ }
+ x += w;
+ len -= w;
+ }
+ return 0;
+}
+
+/* return 0 if closed or error, -1 temporary error */
+static int do_io(void *buf, int len) {
+ int r = read(0,buf,len);
+ if (r<0)
+ if (errno != EINTR) r = 0;
+ if (r>0) xx_write(1,buf,r);
+ return r;
+}
+
+static int mk_backup() {
+ if (flag_rename) {
+ if (rename(name, flag_rename) && errno != ENOENT) return -1;
+ flag_rename = 0;
+ }
+ return 0;
+}
+
+static void write2(char *s) { write(2,s,str_len(s)); }
+
+int main(int argc, char **argv) {
+ unsigned long len=0;
+ int pid, pi[2];
+
+ for (;;) {
+ char *p;
+ argc--;
+ argv++;
+ if ((p=argv[0]) == 0 || *p != '-') break;
+ while (*++p)
+ switch (*p) {
+ case 'a': m |= O_APPEND; break;
+ case 't': m |= O_TRUNC; break;
+ case 'c': m |= O_CREAT; break;
+ case 'r': ++flag_rename; break;
+ case '2': ++flagstderr; break;
+ case '1': ++flagstdout; break;
+ default:
+ goto usage;
+ }
+ }
+
+ if (argc<3) {
+ usage:
+ write2("usage: bootlog [-12ctar] size logfile program args...\n");
+ _exit(1);
+ }
+ if (scan_ulong(argv[0], &len) == 0) goto usage;
+ if ((flagstderr | flagstdout) == 0) {
+ ++flagstdout;
+ ++flagstderr;
+ }
+ name=argv[1];
+
+ for (pid=0; pid<3; pid++)
+ if (fcntl(pid,F_GETFL,0) == -1) goto do_it;
+
+ if (pipe(pi)) goto do_it;
+ while ((pid=fork()) < 0);
+
+ if (pid==0) {
+ close(pi[1]);
+ while ((pid=fork()) < 0);
+ if (pid==0) {
+ dup2(pi[0],0);
+ close(pi[0]);
+ loop(len);
+ }
+ _exit(0);
+ } else {
+ close(pi[0]);
+ waitpid(pid, 0, 0);
+ if (flagstdout) { dup2(pi[1],1); }
+ if (flagstderr) { dup2(pi[1],2); }
+ close(pi[1]);
+ }
+
+ do_it:
+ argv += 2;
+ pathexec_run(argv[0], argv, environ);
+ write2("bootlog: ");
+ write2(argv[0]);
+ write2(": exec error\n");
+ _exit(127);
+}
diff --git a/contrib/bootlog_mmap.c b/contrib/bootlog_mmap.c
new file mode 100644
index 0000000..7bd9583
--- /dev/null
+++ b/contrib/bootlog_mmap.c
@@ -0,0 +1,96 @@
+/* bootlog.c */
+/* diet -Os gcc -o bootlog bootlog.c -Wall -W */
+
+#include <unistd.h>
+#include <alloca.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h> /* rename */
+#include <sys/mman.h>
+#include <sys/wait.h>
+#include "../ninitfeatures.h"
+
+static int xx_write(int fd, void *buf, size_t len);
+static int do_io(void *buf, int len);
+static int mk_backup();
+
+struct mem {
+ struct mem *x;
+ unsigned int p;
+};
+
+static struct mem *last, *root;
+static char *flag_rename, *name;
+static int flagstderr, flagstdout, m;
+int fd = -1;
+
+#include "../pagesize_defs.h"
+#define mem_size sizeof(struct mem)
+#define alloc_size (page_size - mem_size)
+
+void *mmap_alloc() {
+ struct mem *m;
+ m=mmap(0,page_size,PROT_READ|PROT_WRITE,MAP_ANONYMOUS|MAP_PRIVATE,-1,0);
+ if (m==(struct mem*)-1) return 0;
+ /* kernel must zeroed m->p and m->x */
+
+ if (last) last->x = m;
+ else root = m;
+ last = m;
+ return ((void *)m) + mem_size;
+}
+
+static void flush_root() {
+ if (mk_backup()) return;
+ if (fd < 0) fd = open(name, O_WRONLY | m, 0644);
+ if (fd < 0) return;
+ while (root) {
+ void *x = root->x;
+ xx_write(fd, ((void *)root) + mem_size, root->p);
+ root->p = 0;
+ if (root == last) break;
+ munmap(root, page_size);
+ root = x;
+ }
+}
+
+static void loop(unsigned long len) {
+ char *buf = 0;
+ int r;
+ if (flag_rename) {
+ char *d, *s = name;
+ d = flag_rename = alloca(str_len(name) + 5);
+ while (*s) *d++ = *s++;
+ d[0] = '~';
+ d[1] = 0;
+ }
+ while (len) {
+ if (buf == 0 && (buf=mmap_alloc()) == 0) break;
+
+ if (last->p >= alloc_size) {
+ flush_root();
+ if (last->p) { buf = 0; continue; }
+ }
+
+ r = do_io(buf + last->p, alloc_size - last->p);
+ if (r==0) break;
+ if (r<0) continue;
+
+ if ((unsigned long)r > len) r = len;
+ last->p += r;
+ len -= r;
+ }
+
+ if (buf==0 || len==0) {
+ char tmp[1024];
+ while (do_io(tmp,sizeof(tmp)));
+ }
+
+ mk_backup();
+ flag_rename = 0;
+ flush_root();
+ fsync(fd);
+ close(fd);
+}
+
+#include "bootlog.h"
diff --git a/contrib/bootlog_sbrk.c b/contrib/bootlog_sbrk.c
new file mode 100644
index 0000000..525fcdc
--- /dev/null
+++ b/contrib/bootlog_sbrk.c
@@ -0,0 +1,73 @@
+/* bootlog.c */
+/* diet -Os gcc -o bootlog bootlog.c -Wall -W */
+
+#include <unistd.h>
+#include <alloca.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h> /* rename */
+#include <sys/wait.h>
+#include "../ninitfeatures.h"
+#define BRKINCR 1024
+
+static int xx_write(int fd, void *buf, size_t len);
+static int do_io(void *buf, int len);
+static int mk_backup();
+
+static char *root, *last, *end;
+static char *flag_rename, *name;
+static int flagstderr, flagstdout, m;
+int fd = -1;
+
+static void *setbrk(ssize_t incr) {
+ void *x = sbrk(incr);
+ if ((void *)-1 == x) return x;
+ if (root == 0) root = last = x;
+ end = x + incr;
+ return 0;
+}
+
+static void flush_root() {
+ if (mk_backup()) return;
+ if (fd < 0) fd = open(name, O_WRONLY | m, 0644);
+ if (fd < 0 || root==0) return;
+ xx_write(fd, root, last-root);
+ last = root;
+ setbrk((ssize_t)BRKINCR - (end-root));
+}
+
+static void loop(unsigned long len) {
+ int r;
+ if (flag_rename) {
+ char *d, *s = name;
+ d = flag_rename = alloca(str_len(name) + 5);
+ while (*s) *d++ = *s++;
+ d[0] = '~';
+ d[1] = 0;
+ }
+ while (len) {
+ if (last >= end) flush_root();
+ if (last >= end && setbrk(BRKINCR)) break;
+
+ r = do_io(last, end - last);
+ if (r==0) break;
+ if (r<0) continue;
+
+ if ((unsigned long)r > len) r = len;
+ last += r;
+ len -= r;
+ }
+
+ if (last >= end || len==0) {
+ char tmp[1024];
+ while (do_io(tmp,sizeof(tmp)));
+ }
+
+ mk_backup();
+ flag_rename = 0;
+ flush_root();
+ fsync(fd);
+ close(fd);
+}
+
+#include "bootlog.h"
diff --git a/contrib/conditional-init.c b/contrib/conditional-init.c
new file mode 100644
index 0000000..10c69cc
--- /dev/null
+++ b/contrib/conditional-init.c
@@ -0,0 +1,47 @@
+#include <unistd.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/reboot.h>
+#include "../ninitfeatures.h"
+#include "../uid.h"
+
+#define TM_OUT 1200
+
+int main(int argc, char **argv) {
+ unsigned long ul;
+ char *stable, *test;
+
+ if (argc < 4) {
+ carp("usage: conditional-init now /stable/init /test/init [args...]\n"
+ "\tnow must be the output of: date +%s");
+ return 1;
+ }
+
+ errmsg_argv0 = "conditional-init";
+ stable=argv[2];
+ test=argv[3];
+
+ if (scan_ulong(argv[1], &ul)==0 || ul + TM_OUT < (unsigned long)time(0)) {
+ carp("booting (stable): ", stable);
+ argv += 3;
+ argv[0] = stable;
+ execve(stable, argv, environ);
+ return 0;
+ }
+
+ if (fork()==0) {
+ unsigned long deadline = (unsigned long)time(0) + TM_OUT;
+ while ((unsigned long)time(0) < deadline ) nano_sleep(1,0);
+ sync();
+ set_reboot(RB_AUTOBOOT);
+ return 2;
+ }
+
+ argv += 3;
+ argv[0]=test;
+ carp("booting (test): ", test);
+ execve(test, argv, environ);
+ nano_sleep(TM_OUT,0); /* only if test fails */
+ set_reboot(RB_AUTOBOOT);
+ return 1;
+}
diff --git a/contrib/env.c b/contrib/env.c
new file mode 100644
index 0000000..5bc44bd
--- /dev/null
+++ b/contrib/env.c
@@ -0,0 +1,68 @@
+#include <unistd.h>
+#include <alloca.h>
+#include "../ninitfeatures.h"
+#include "../djb/buffer.h"
+#include "../error_table.h"
+
+static unsigned long env_free;
+#include "put_env.h"
+
+#define BUFFER_1_SIZE 1200
+buffer b = BUFFER_INIT(write, 2, 0, BUFFER_1_SIZE);
+
+#undef die
+#define die(n,...) err_b(&b,__VA_ARGS__,0); buffer_flush(&b); _exit(n)
+
+int main(int argc,char *argv[]) {
+ int i;
+ char *nenv, **env;
+
+ b.x=alloca(BUFFER_1_SIZE); /* XXX if it fails bummer */
+ errmsg_iam("env");
+ if (environ==0) environ = argv+argc;
+ for (env=environ; *env; ++env) env_free++;
+
+ env=alloca((argc+env_free+3) * sizeof (char*));
+ if (env==0) { die(1, "Out of memory"); }
+
+ byte_copy(env, (env_free+1) * sizeof(char *), environ);
+ environ = env;
+ env_free = argc;
+
+ for (i=1; i<argc; ++i) {
+ char *v=argv[i];
+ if (v[0]=='-') {
+ if (v[1]==0) {
+ environ[0]=0;
+ } else {
+ for (++v; *v; ++v)
+ switch (*v) {
+ case 'i': environ[0]=0; break;
+ case 'u':
+ if (v[1]) { nenv = v+1; goto do_it; }
+ else if (argv[++i]) { nenv = argv[i]; goto do_it; }
+ default:
+ die(1, "usage: env [-] [-i] [-u] [NAME=VALUE...] "
+ "[program args...]");
+ }
+ }
+ } else {
+ if (v[str_chr(v,'=')]) { nenv = v; goto do_it; }
+ else {
+ pathexec_run(v,argv+i,environ);
+ die(127, "unable ro run: ",v,": ",error_string(table,errno));
+ }
+ }
+ continue;
+
+ do_it:
+ put_env(nenv);
+ }
+
+ for (b.fd = 1; *environ; ++environ) {
+ buffer_puts(&b, *environ);
+ buffer_puts(&b, "\n");
+ }
+ buffer_flush(&b);
+ return 0;
+}
diff --git a/contrib/put_env.h b/contrib/put_env.h
new file mode 100644
index 0000000..0d817b3
--- /dev/null
+++ b/contrib/put_env.h
@@ -0,0 +1,27 @@
+int put_env(const char *string) {
+ unsigned int len, envc, remove=0;
+ char **ep;
+
+ len=str_chr(string,'=');
+ if (string[len]==0) remove=1;
+ for (envc=0, ep=environ; *ep; ++ep) {
+ if (!byte_diff(string, len, *ep) && (*ep)[len]=='=') {
+ if (remove) {
+ for (; ep[1]; ++ep) ep[0]=ep[1];
+ ep[0]=0;
+ ++env_free;
+ return 0;
+ }
+ *ep=(char *)string;
+ return 0;
+ }
+ ++envc;
+ }
+ if (remove==0) {
+ if (env_free==0) return -1;
+ environ[envc++]=(char*)string;
+ environ[envc]=0;
+ --env_free;
+ }
+ return 0;
+}
diff --git a/contrib/serdo.c b/contrib/serdo.c
new file mode 100644
index 0000000..e45fb9a
--- /dev/null
+++ b/contrib/serdo.c
@@ -0,0 +1,236 @@
+/* serdo.c
+ modified by Nikola Vladov
+ unset VAR
+ exit n
+ exec file
+ # comment
+ ps -ax # comment ps
+ long line \
+ continues here
+ echo something
+ . file
+ VAR=val ... command ...
+ killall5 -signumber
+ */
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+#include <time.h>
+#include "../ninitfeatures.h"
+#include "../error_table.h"
+
+struct cmd {
+ char *pos;
+ char *x;
+ char last;
+ char eof;
+};
+
+static unsigned int env_free, last_cmd;
+static char continueonerror;
+static int batch(char *s);
+
+#include "put_env.h"
+
+static char *e() { return error_string(table, errno); }
+
+#define str_equal(A,B) !str_diff(A,B)
+#define byte_equal(A,l,B) !byte_diff(A,l,B)
+#define is_space(c) *c==' ' || (unsigned int)(char)(*c - 9)<5
+#define carpsys(...) err(2,__VA_ARGS__,": ",e(),(char*)0)
+#define diesys(n,...) do { err(2,__VA_ARGS__,": ",e(),(char*)0); _exit(n); } while(0)
+
+#define DQUOTE '"'
+#define SQUOTE '\''
+
+static void pr_argv(int fd, char **argv) {
+ char *s;
+ while ((s=*argv++)) {
+ unsigned int len = str_len(s);
+ if (*argv) s[len++] = ' ';
+ errmsg_put(fd,s,len);
+ }
+ errmsg_puts(fd,"\n");
+ errmsg_puts(fd,0);
+}
+
+static int pr_fail(char **argv) {
+ carpsys(argv[0]);
+ if (argv[1]) pr_argv(2, argv);
+ return -1;
+}
+
+static void put__env(char *s) { if (put_env(s)) die(1, "Out of memory"); }
+
+static int spawn(char **argv) {
+ int i;
+ char *s0=argv[0], *s1=argv[1], cfg_exec=0, ignore=0;
+
+ if (str_equal(s0,"cd")) {
+ if (chdir(s1)) return pr_fail(argv);
+ return 0;
+ } else if (str_equal(s0,"export") || str_equal(s0,"unset")) {
+ while (*++argv) put__env(*argv);
+ return 0;
+#ifdef SERDO_WANT_echo
+ } else if (str_equal(s0,"echo")) {
+ pr_argv(1, argv+1);
+ return 0;
+#endif
+ } else if (s1) {
+ i = x_atoi(s1);
+ if (str_equal(s0,"exit")) { _exit(i); }
+ else if (str_equal(s0,"exec")) { cfg_exec++; ++argv; }
+ else if (s0[0]=='.' && s0[1]==0) { return batch(s1); }
+#ifdef SERDO_WANT_killall5
+ else if (str_equal(s0,"killall5")) { return kill(-1,-i); }
+#endif
+ }
+
+ if (argv[0][0] == '-') { argv[0] += 1; ignore = 1; }
+
+ if (last_cmd && !cfg_exec) {
+ struct timespec ts = { 0, 500000000 };
+ while ((i=fork()) < 0) nanosleep(&ts, 0);
+ } else i=-1; /* don't fork */
+
+ if (i <= 0) {
+#ifdef SERDO_WANT_environ_in_command
+ for (; argv[1]; put__env(*argv++)) {
+ s0 = argv[0];
+ if (!s0[str_chr(s0,'=')]) break;
+ }
+#endif
+ pathexec_run(*argv,argv,environ);
+ pr_fail(argv);
+ if (!i) _exit(-1); /* child */
+ return (ignore) ? 0 : -1;
+ }
+
+ if (waitpid(i,&i,0)==-1) diesys(1,"waitpid failed");
+ if (ignore) return 0;
+ if (!WIFEXITED(i)) return -1;
+ return WEXITSTATUS(i);
+}
+
+static void get_word(struct cmd *c) {
+ char ch, *x, *s = c->pos;
+
+ while (is_space(s) || (*s=='\\' && s[1]=='\n')) ++s;
+ if (!*s) { c->eof |= 1; return; }
+ c->last=0;
+ c->x=x=s;
+ if (*x == '#') c->last |= 2;
+
+ while ((ch=*s)) {
+ if (ch==DQUOTE || ch==SQUOTE) {
+ while (*++s) { /* unterminated quoted string <==> *s == 0 */
+ if (*s != ch) *x++ = *s;
+ else {
+ if (s[-1] == '\\') x[-1] = ch;
+ else { s++; break; }
+ }
+ }
+ if (!*s) die(2, "syntax error: unexpected EOF");
+ }
+ else if (ch==' ' || ch=='\n' || ch=='\t') break;
+ else if (ch=='\\' && s[1]=='\n') { s += 2; continue; }
+ else if (ch=='#') { for (; *s && *s != '\n';) s++; break; }
+ else *x++ = *s++;
+ }
+ while (*s==' ' || *s=='\t') ++s;
+ if (*s=='\n') { ++s; c->last |= 1; }
+
+ c->pos = s;
+ if (!*s) c->last |= 1;
+ *x = 0;
+}
+
+static char *get_argv0(struct cmd *c) {
+ while (1) {
+ get_word(c);
+ if (c->eof) break;
+ if (c->last & 2) continue;
+ return c->x;
+ }
+ return 0;
+}
+
+static int get_argv(struct cmd *c) {
+ unsigned int k=0, len=16;
+ char **argv, first=0;
+
+ argv = alloca((len+2)*sizeof(char*));
+ do {
+ if (first) get_word(c);
+ else first |= 1;
+ if (c->eof) break;
+ if (c->last < 2) { /* comment */
+ if (k >= len) {
+ char **tmp = argv;
+ len *= 2;
+ argv = alloca((len+2) * sizeof(char*));
+ byte_copy(argv, k*sizeof(char*), tmp);
+ }
+ argv[k++] = c->x;
+ }
+ } while (!c->last);
+ argv[k] = 0;
+
+ if (!get_argv0(c)) --last_cmd;
+ return spawn(argv);
+}
+
+static int execute(struct cmd *c) {
+ int r = 0;
+ if (get_argv0(c)) {
+ ++last_cmd;
+
+ while (1) {
+ r = get_argv(c);
+ if (r!=0 && !continueonerror) break;
+ if (c->eof) break;
+ }
+ }
+ return r;
+}
+
+static int batch(char *s) {
+ struct cmd c;
+ int len, fd=open(s,O_RDONLY);
+ if (fd==-1 || GLOBAL_READ(fd,c.pos, len,32768)) {
+ carpsys("could not open ",s);
+ return 1;
+ }
+ close(fd);
+ c.pos[len] = 0;
+ c.eof = 0;
+ return execute(&c);
+}
+
+int main(int argc,char* argv[],char* env[]) {
+ if (argc<2) {
+#ifdef SERDO_EXEC_script
+ if (!access("script",O_RDONLY)) *argv-- = "script"; else
+#endif
+ { ops: die(1,"usage: serdo [-c] file"); }
+ }
+
+ if (str_equal(argv[1],"-c")) {
+ ++continueonerror;
+ ++argv;
+ }
+ if (*++argv == 0) goto ops;
+ errmsg_iam("serdo");
+
+ for (env_free=0; env[env_free];) ++env_free;
+ ++env_free;
+ environ = alloca((env_free + SERDO_MAX_NEW_ENVIRON) * sizeof(char *));
+ byte_copy(environ, env_free * sizeof(char *), env);
+ env_free = SERDO_MAX_NEW_ENVIRON;
+
+ return batch(*argv);
+}
diff --git a/contrib/sleeprun.c b/contrib/sleeprun.c
new file mode 100644
index 0000000..01ea5c9
--- /dev/null
+++ b/contrib/sleeprun.c
@@ -0,0 +1,65 @@
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/types.h>
+#include <signal.h>
+#include "../ninitfeatures.h"
+#include "../error_table.h"
+
+int main(int argc, char **argv) {
+ int fd;
+ unsigned long now, interval, last, ul[2] = { 0, 0 };
+ char tmp[32],*p;
+ if (argc<3)
+ die(1, "usage: sleeprun SleepFile interval [program args...]\n"
+ "usage: sleeprun -aNumber program [args...]");
+ errmsg_iam(argv[0]);
+
+ p = argv[1];
+ if (p[0] == '-' && p[1] == 'a') {
+ scan_ulong(p+2, &interval);
+ if (interval) alarm(interval);
+ argv += 2;
+ goto do_it;
+ }
+
+ if (read_ulongs(argv[1], ul, 2) > 0) {
+ errno=0;
+ if (kill(ul[0],0)==0 || errno != ESRCH) {
+ tmp[fmt_ulong(tmp, ul[0])] = 0;
+ carp("WARNING: a program with PID ",tmp," is running now");
+ }
+ }
+
+ last=ul[1];
+ scan_ulong(argv[2], &interval);
+ now = (unsigned long)time(0);
+
+ last += interval;
+ if (last > now) {
+ nano_sleep(last-now, 0);
+ now = (unsigned long)time(0);
+ }
+
+ if ((fd = open(argv[1], O_WRONLY | O_CREAT | O_TRUNC, 0644)) >=0) {
+ p = tmp;
+ p += fmt_ulong(tmp, getpid()); *p++ = ':';
+ p += fmt_ulong(p, now); *p++ = 0;
+ write(fd,tmp,p-tmp);
+ close(fd);
+ }
+
+ argv+=3;
+ do_it:
+ if (argv[0]) {
+ char *argv_0 = argv[0];
+ argv[0] = argv_0 + str_rchr(argv_0,'/');
+ if (argv[0][0]) argv[0]++;
+ else argv[0]=argv_0;
+
+ pathexec_run(argv_0,argv,environ);
+ carp("unable to run: ",argv_0,": ",error_string(table, errno));
+ return 127;
+ }
+ return 0;
+}
diff --git a/cron.c b/cron.c
new file mode 100644
index 0000000..05a94a8
--- /dev/null
+++ b/cron.c
@@ -0,0 +1,27 @@
+#include <time.h>
+#include "ninitfeatures.h"
+
+void cron(char **argv, unsigned long *ret) /*EXTRACT_INCL*/ {
+ unsigned long a,b,n, cron=0, now=time(0);
+ unsigned long u[3];
+ int dummy;
+ char *p;
+
+ for (; *argv; argv++) {
+ p = *argv;
+ u[2] = 0;
+ if (scan_ulongs(p,u,3, scan_sec,':',&dummy) < 2) continue;
+
+ if ((a=u[0])==0 || a > (1<<29)) continue;
+ n = now;
+ b = u[1];
+
+ if ((a % 604800) == 0) { b += 3*86400; n -= 3*86400; }
+ b %= a;
+ n = n + b - (n % a);
+ while (n <= now) n += a;
+
+ if (cron==0 || n < cron) { cron = n; ret[1] = u[2]; }
+ }
+ ret[0] = cron;
+}
diff --git a/djb/README b/djb/README
new file mode 100644
index 0000000..de175ef
--- /dev/null
+++ b/djb/README
@@ -0,0 +1,2 @@
+ninit uses small string/buffer operations.
+In most cases they are less than 15 bytes.
diff --git a/djb/atoulong.c b/djb/atoulong.c
new file mode 100644
index 0000000..95c96d4
--- /dev/null
+++ b/djb/atoulong.c
@@ -0,0 +1,7 @@
+unsigned int atoulong(char *s) /*EXTRACT_INCL*/ {
+ register unsigned int dest=0;
+ register unsigned char c;
+
+ while ((c=*s-'0')<10) { ++s; dest=dest*10 + c; }
+ return dest;
+}
diff --git a/djb/buffer.h b/djb/buffer.h
new file mode 100644
index 0000000..dd920f6
--- /dev/null
+++ b/djb/buffer.h
@@ -0,0 +1,16 @@
+#ifndef BUFFER_H
+#define BUFFER_H
+
+typedef struct buffer {
+ char *x; /* actual buffer space */
+ unsigned int p; /* current position */
+ unsigned int n; /* string position */
+ unsigned int a; /* allocated buffer size */
+ int fd;
+ int (*op)();
+} buffer;
+
+#define BUFFER_INIT(op,fd,buf,len) { (buf), 0, 0, (len), (fd), (int (*)())(op) }
+#include "../buffer_defs.h"
+#include "../byte_defs.h"
+#endif
diff --git a/djb/buffer_get.c b/djb/buffer_get.c
new file mode 100644
index 0000000..ed51707
--- /dev/null
+++ b/djb/buffer_get.c
@@ -0,0 +1,17 @@
+#include "buffer.h"
+#include <errno.h>
+
+int buffer_getc(buffer* b, char *s) /*EXTRACT_INCL*/ {
+ if (b->p >= b->n) {
+ int r;
+ while ((r=b->op(b->fd, b->x, b->a)) <0)
+ if (errno != EINTR) break;
+
+ if (r<=0) return r;
+ b->p = 0;
+ b->n = r;
+ }
+ *s = b->x[b->p];
+ b->p++;
+ return 1;
+}
diff --git a/djb/buffer_put.c b/djb/buffer_put.c
new file mode 100644
index 0000000..f67e98d
--- /dev/null
+++ b/djb/buffer_put.c
@@ -0,0 +1,37 @@
+#include <errno.h>
+#include "buffer.h"
+
+#ifdef USE_BUFFER_LARGE_WRITE
+#define X(a) (a>0xffffff)?0xffffff:a
+#else
+#define X(a) a
+#endif
+
+static int allwrite(buffer *b, const char *buf, unsigned int len) {
+ int w;
+ b->p = 0;
+ while (len) {
+ w = b->op(b->fd, buf, X(len));
+ if (w == -1) {
+ if (errno == EINTR) continue;
+ return -1;
+ }
+ buf += w;
+ len -= w;
+ }
+ return 0;
+}
+
+int buffer_flush(buffer *b) /*EXTRACT_INCL*/ {
+ return allwrite(b,b->x,b->p);
+}
+
+int buffer_put(buffer *b, const char* s, unsigned int len) /*EXTRACT_INCL*/ {
+ if (b->a-b->p < len) {
+ if (buffer_flush(b)==-1) return -1;
+ if (b->a < len) return allwrite(b, s, len);
+ }
+ byte_copy(b->x + b->p, len, s);
+ b->p += len;
+ return 0;
+}
diff --git a/djb/buffer_putc.c b/djb/buffer_putc.c
new file mode 100644
index 0000000..5ed0689
--- /dev/null
+++ b/djb/buffer_putc.c
@@ -0,0 +1,5 @@
+#include "buffer.h"
+
+int buffer_putc(buffer *b, char ch) /*EXTRACT_INCL*/ {
+ return buffer_put(b, &ch, 1);
+}
diff --git a/djb/buffer_puts.c b/djb/buffer_puts.c
new file mode 100644
index 0000000..0a8696e
--- /dev/null
+++ b/djb/buffer_puts.c
@@ -0,0 +1,6 @@
+#include "buffer.h"
+
+int buffer_puts(buffer *b, const char* s) /*EXTRACT_INCL*/ {
+ return buffer_put(b, s, str_len(s));
+}
+
diff --git a/djb/byte_copy.c b/djb/byte_copy.c
new file mode 100644
index 0000000..84d9f45
--- /dev/null
+++ b/djb/byte_copy.c
@@ -0,0 +1,7 @@
+void byte_copy(void *to, unsigned int n, const void *from) /*EXTRACT_INCL*/ {
+ char *d=(char*)to;
+ char *s=(char*)from;
+ unsigned int k=0;
+ for (; k<n; k++) d[k] = s[k];
+}
+
diff --git a/djb/byte_copyr.c b/djb/byte_copyr.c
new file mode 100644
index 0000000..9574ae7
--- /dev/null
+++ b/djb/byte_copyr.c
@@ -0,0 +1,8 @@
+void byte_copyr(void* dst, unsigned int n, const void* src) /*EXTRACT_INCL*/ {
+ char *d=(char*)dst;
+ char *s=(char*)src;
+ while (n) {
+ --n;
+ d[n] = s[n];
+ }
+}
diff --git a/djb/byte_diff.c b/djb/byte_diff.c
new file mode 100644
index 0000000..6ad0a13
--- /dev/null
+++ b/djb/byte_diff.c
@@ -0,0 +1,11 @@
+int byte_diff(const void* a, unsigned int len, const void* b) /*EXTRACT_INCL*/ {
+ char *x=(char *)a;
+ char *y=(char *)b;
+ unsigned int u=0;
+ char ch=0;
+ for (; u<len; u++) {
+ ch = x[u] - y[u];
+ if (ch) break;
+ }
+ return ch;
+}
diff --git a/djb/byte_set.c b/djb/byte_set.c
new file mode 100644
index 0000000..0248efa
--- /dev/null
+++ b/djb/byte_set.c
@@ -0,0 +1,7 @@
+void byte_set(const void* dst, unsigned int len, char ch) /*EXTRACT_INCL*/ {
+ char *d=(char*)dst;
+ while (len) {
+ --len;
+ d[len] = ch;
+ }
+}
diff --git a/djb/byte_zero.c b/djb/byte_zero.c
new file mode 100644
index 0000000..1fba78a
--- /dev/null
+++ b/djb/byte_zero.c
@@ -0,0 +1,7 @@
+void byte_zero(void *to, unsigned int k) /*EXTRACT_INCL*/ {
+ char *d = (char *)to;
+ while (k) {
+ k--;
+ d[k] = 0;
+ }
+}
diff --git a/djb/env_get.c b/djb/env_get.c
new file mode 100644
index 0000000..f84a1fa
--- /dev/null
+++ b/djb/env_get.c
@@ -0,0 +1,15 @@
+#include "../byte_defs.h"
+extern char **environ;
+
+char *env_get(const char *s) /*EXTRACT_INCL*/ {
+ int i;
+ unsigned int slen;
+ char *envi;
+
+ if (environ==0) return 0;
+ slen = str_len(s);
+ for (i = 0; (envi = environ[i]); ++i)
+ if ((!byte_diff(s,slen,envi)) && (envi[slen] == '='))
+ return envi + slen + 1;
+ return 0;
+}
diff --git a/djb/fmt_str.c b/djb/fmt_str.c
new file mode 100644
index 0000000..3843311
--- /dev/null
+++ b/djb/fmt_str.c
@@ -0,0 +1,8 @@
+unsigned int fmt_str(char *s, const char *t) /*EXTRACT_INCL*/ {
+ register unsigned int len;
+ char ch;
+ len = 0;
+ if (s) { while ((ch = t[len])) s[len++] = ch; }
+ else while (t[len]) len++;
+ return len;
+}
diff --git a/djb/fmt_ulong.c b/djb/fmt_ulong.c
new file mode 100644
index 0000000..dfd7f9e
--- /dev/null
+++ b/djb/fmt_ulong.c
@@ -0,0 +1,10 @@
+unsigned int fmt_ulong(char *s, unsigned long u) /*EXTRACT_INCL*/ {
+ register unsigned int len; register unsigned long q;
+ len = 1; q = u;
+ while (q > 9) { ++len; q /= 10; }
+ if (s) {
+ s += len;
+ do { *--s = '0' + (u % 10); u /= 10; } while(u); /* handles u == 0 */
+ }
+ return len;
+}
diff --git a/djb/scan_8ulong.c b/djb/scan_8ulong.c
new file mode 100644
index 0000000..1999e29
--- /dev/null
+++ b/djb/scan_8ulong.c
@@ -0,0 +1,5 @@
+#include "scan_number.h"
+SCAN_NUMBER_DEFINE(scan_8ulong, unsigned long, 8)
+#if 0
+unsigned int scan_8ulong(const char *s, unsigned long *u) /*EXTRACT_INCL*/
+#endif
diff --git a/djb/scan_number.h b/djb/scan_number.h
new file mode 100644
index 0000000..17e1586
--- /dev/null
+++ b/djb/scan_number.h
@@ -0,0 +1,9 @@
+#define SCAN_NUMBER_DEFINE(name, type, base) \
+unsigned int name(const char *s, type *u) {\
+ unsigned int pos;\
+ type result, c;\
+ pos = 0; result = 0;\
+ while ((c = (type) (unsigned char) (s[pos] - '0')) < base)\
+ { result = result * base + c; ++pos; }\
+ *u = result; return pos;\
+}
diff --git a/djb/scan_ulong.c b/djb/scan_ulong.c
new file mode 100644
index 0000000..8f240a8
--- /dev/null
+++ b/djb/scan_ulong.c
@@ -0,0 +1,5 @@
+#include "scan_number.h"
+SCAN_NUMBER_DEFINE(scan_ulong, unsigned long, 10)
+#if 0
+unsigned int scan_ulong(const char *s, unsigned long *u) /*EXTRACT_INCL*/
+#endif
diff --git a/djb/str_chr.c b/djb/str_chr.c
new file mode 100644
index 0000000..1506f30
--- /dev/null
+++ b/djb/str_chr.c
@@ -0,0 +1,6 @@
+unsigned int str_chr(const char *in, char needle) /*EXTRACT_INCL*/ {
+ unsigned int u=0;
+ char ch;
+ while ((ch=in[u]) && ch != needle) u++;
+ return u;
+}
diff --git a/djb/str_copy.c b/djb/str_copy.c
new file mode 100644
index 0000000..90d2da6
--- /dev/null
+++ b/djb/str_copy.c
@@ -0,0 +1,5 @@
+unsigned int str_copy(char *out,const char *in) /*EXTRACT_INCL*/ {
+ unsigned int len=0;
+ while ((out[len]=in[len])) len++;
+ return len;
+}
diff --git a/djb/str_copyn.c b/djb/str_copyn.c
new file mode 100644
index 0000000..3dab724
--- /dev/null
+++ b/djb/str_copyn.c
@@ -0,0 +1,6 @@
+unsigned int str_copyn(char *out,const char *in, unsigned int len) /*EXTRACT_INCL*/ {
+ unsigned int k=0;
+ while (k<len && (out[k]=in[k])) k++;
+ if (k<len) out[k] = 0;
+ return k;
+}
diff --git a/djb/str_diff.c b/djb/str_diff.c
new file mode 100644
index 0000000..2c5ba59
--- /dev/null
+++ b/djb/str_diff.c
@@ -0,0 +1,11 @@
+int str_diff(const char* a, const char* b) /*EXTRACT_INCL*/ {
+ unsigned int u=0;
+ char ch=0;
+ for (;; u++) {
+ ch = a[u]-b[u];
+ if (ch) break;
+ if (a[u]==0) break;
+ }
+ return ch;
+}
+
diff --git a/djb/str_diffn.c b/djb/str_diffn.c
new file mode 100644
index 0000000..934040f
--- /dev/null
+++ b/djb/str_diffn.c
@@ -0,0 +1,11 @@
+int str_diffn(const char* a, const char* b, unsigned int limit) /*EXTRACT_INCL*/ {
+ unsigned int u=0;
+ char ch=0;
+
+ for (; u<limit; u++) {
+ ch = a[u] - b[u];
+ if (ch) break;
+ if (a[u]==0) break;
+ }
+ return ch;
+}
diff --git a/djb/str_len.c b/djb/str_len.c
new file mode 100644
index 0000000..9ce761a
--- /dev/null
+++ b/djb/str_len.c
@@ -0,0 +1,5 @@
+unsigned int str_len(const char * s) /*EXTRACT_INCL*/ {
+ unsigned int len=0;
+ while (s[len]) ++len;
+ return len;
+}
diff --git a/djb/str_rchr.c b/djb/str_rchr.c
new file mode 100644
index 0000000..ce7ab37
--- /dev/null
+++ b/djb/str_rchr.c
@@ -0,0 +1,9 @@
+unsigned int str_rchr(const char *in, char needle) /*EXTRACT_INCL*/ {
+ char ch;
+ unsigned int u=0, found = (unsigned int)-1;
+ for (;; u++) {
+ if ((ch=in[u])==0) break;
+ if (ch==needle) found=u;
+ }
+ return (found != (unsigned int)-1) ? found : u;
+}
diff --git a/error_table.h b/error_table.h
new file mode 100644
index 0000000..8221a90
--- /dev/null
+++ b/error_table.h
@@ -0,0 +1,19 @@
+#include <errno.h>
+
+static struct error_table table[] = {
+ {EACCES, "Permission denied"},
+ {EINVAL, "Invalid argument"},
+ {EIO, "I/O error"},
+ {EISDIR, "Is a directory"},
+ {ELOOP, "Too many symbolic links"},
+ {ENAMETOOLONG, "File name too long"},
+ {ENOENT, "No such file or directory"},
+ {ENOEXEC, "Exec format error"},
+ {ENOMEM, "Out of memory"},
+ {ENOSYS, "Function not implemented"},
+ {ENOTDIR, "Not a directory"},
+ {EROFS, "Read-only file system"},
+ {ETXTBSY, "Text file busy"},
+ {ESPIPE, "Illegal seek"},
+ {0,0}
+};
diff --git a/findservice.h b/findservice.h
new file mode 100644
index 0000000..29f3f1a
--- /dev/null
+++ b/findservice.h
@@ -0,0 +1,24 @@
+#ifdef INIT_PROGRAM
+static void circsweep() {
+ int i;
+ for (i=0; i<=maxprocess; i++)
+ root[i].pr_circular=0;
+}
+#endif
+
+
+/* return index of service in process data structure or -1 if not found */
+static int findservice(char *service) {
+ int i;
+
+ for (i=0; i<=maxprocess; ++i) {
+#if defined (INIT_PROGRAM) && ! defined (INIT_BLACK)
+ char *x=root_name(i), *y=service;
+ while (*x == *y && *x) { x++; y++; }
+ if (*x==*y) return i;
+#else
+ if (!str_diff(root_name(i), service)) return i;
+#endif
+ }
+ return -1;
+}
diff --git a/get_headers b/get_headers
new file mode 100755
index 0000000..a18b474
--- /dev/null
+++ b/get_headers
@@ -0,0 +1,25 @@
+#!/bin/sh
+[ $# -lt 2 ] && exit 1
+
+export PATH=/bin:/usr/bin
+name=`echo $1 | sed -e 's/\./__DOT__/g' -e 's/\//__SLASH__/g'`
+shift
+
+echo '#ifndef' AUTO_FILE__$$__$name
+echo '#define' AUTO_FILE__$$__$name
+echo '/* '`date`' */'
+
+while test $# -gt 0; do
+ case $1 in
+ -L*) echo $1 | sed -e 's/^../#include "/' -e 's/$/"/' ; shift;;
+ -G*) echo $1 | sed -e 's/^../#include </' -e 's/$/>/' ; shift;;
+ -I*) echo $1 | sed -e 's/^../#include /'; shift;;
+ *) break;;
+ esac
+done
+
+if [ $# -gt 0 ] ; then
+ sed -n -e 's/\(.*\) \/\*EXTRACT_INCL\*\/.*/extern \1;/p' \
+ -e 's/ \/\*EXTRACT_UNMOD\*\/.*//p' $@
+fi
+echo '#endif'
diff --git a/get_services.h b/get_services.h
new file mode 100644
index 0000000..d64ae55
--- /dev/null
+++ b/get_services.h
@@ -0,0 +1,91 @@
+#include "djb/buffer.h"
+
+buffer b_outfd = BUFFER_INIT(read, -1, 0, BUFFER_TMP_LEN);
+static unsigned short maxhistory;
+static unsigned short *history;
+
+#ifdef UNKNOWN_PROCESS_SIZE
+static unsigned int process_size;
+#else
+#define process_size PROCESS_SIZE
+#endif
+
+#define MAX_SERVICE_NAME 256
+
+/* read from outfd string or constant lenght*/
+int read_stringb(char *s,int len) {
+ int count=0;
+ while (1) {
+ if (buffer_getc(&b_outfd, s+count) <=0 ) break;
+ if (len==-1 && s[count]==0) return count;
+ ++count;
+ if (count==len) return count;
+ if (len==-1 && count==MAX_SERVICE_NAME) break;
+ }
+ die_xx("error getting data");
+ return -1;
+}
+
+/* buf[??] is defined in the main program */
+void get_services() {
+ int j;
+ char x[MAX_SERVICE_NAME + PROCESS_SIZE], *tmp, *name="";
+
+ if (root) return;
+ b_outfd.x = buf;
+ b_outfd.fd = outfd;
+ x[0] = 'D', x[1]='0';
+ if (do_update) x[1]='1'; /* XXX D1 set doupdate */
+ write(infd,x,2);
+
+ /* 1. names */
+ while (1) {
+ j = read_stringb(x,-1);
+ if (j==0) break;
+ if ((int)mem.r <= j + PROCESS_SIZE) die_xx("Out of memory");
+ mem.r -= ++j;
+ name = mem.x + mem.r;
+ byte_copy(name, j, x);
+ ++maxprocess;
+ }
+
+ /* 2. history */
+ read_stringb(x, sizeof(unsigned short) - 1);
+ maxhistory = (unsigned char)x[0];
+ j = sizeof(unsigned short) * maxhistory;
+
+#ifdef UNKNOWN_PROCESS_SIZE
+ if (process_size && maxprocess >= 0) {
+ process_size -= (mem.a-mem.r) + j + sizeof(unsigned short);
+ process_size /= (maxprocess+1);
+ byte_zero(mem.x, mem.r);
+ } else
+ process_size = PROCESS_SIZE;
+#endif
+
+ mem.r -= (mem.r % 8);
+ if (j) {
+ if ((int)mem.r < j) die_xx("Out of memory");
+ mem.r -= j;
+ tmp = mem.x + mem.r;
+ read_stringb(tmp,j);
+ history = (unsigned short *)tmp;
+ }
+
+ /* 3. processes */
+ mem.l = (maxprocess+1) * PROCESS_SIZE;
+ mem.r -= (mem.r % 16);
+ if (mem.l >= mem.r) die_xx("Out of memory");
+ tmp = mem.x + mem.r - mem.l;
+ root = (struct process *)tmp;
+
+ for (j=0; j<=maxprocess; j++) {
+ read_stringb(tmp, process_size);
+ tmp += PROCESS_SIZE;
+ root[j].pr_circular = 0;
+ root[j].name = alloc_name_set(name);
+ name += str_len(name) + 1;
+ }
+}
+
+#undef MAX_SERVICE_NAME
diff --git a/home.tar.gz b/home.tar.gz
new file mode 100644
index 0000000..3051812
--- /dev/null
+++ b/home.tar.gz
Binary files differ
diff --git a/initreq.h b/initreq.h
new file mode 100644
index 0000000..6f6547b
--- /dev/null
+++ b/initreq.h
@@ -0,0 +1,77 @@
+/*
+ * initreq.h Interface to talk to init through /dev/initctl.
+ *
+ * Copyright (C) 1995-2004 Miquel van Smoorenburg
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * Version: @(#)initreq.h 1.28 31-Mar-2004 MvS
+ *
+ */
+#ifndef _INITREQ_H
+#define _INITREQ_H
+
+#include <sys/param.h>
+
+#if defined(__FreeBSD_kernel__)
+# define INIT_FIFO "/etc/.initctl"
+#else
+# define INIT_FIFO "/dev/initctl"
+#endif
+
+#define INIT_MAGIC 0x03091969
+#define INIT_CMD_START 0
+#define INIT_CMD_RUNLVL 1
+#define INIT_CMD_POWERFAIL 2
+#define INIT_CMD_POWERFAILNOW 3
+#define INIT_CMD_POWEROK 4
+#define INIT_CMD_BSD 5
+#define INIT_CMD_SETENV 6
+#define INIT_CMD_UNSETENV 7
+
+#define INIT_CMD_CHANGECONS 12345
+
+#ifdef MAXHOSTNAMELEN
+# define INITRQ_HLEN MAXHOSTNAMELEN
+#else
+# define INITRQ_HLEN 64
+#endif
+
+/*
+ * This is what BSD 4.4 uses when talking to init.
+ * Linux doesn't use this right now.
+ */
+struct init_request_bsd {
+ char gen_id[8]; /* Beats me.. telnetd uses "fe" */
+ char tty_id[16]; /* Tty name minus /dev/tty */
+ char host[INITRQ_HLEN]; /* Hostname */
+ char term_type[16]; /* Terminal type */
+ int signal; /* Signal to send */
+ int pid; /* Process to send to */
+ char exec_name[128]; /* Program to execute */
+ char reserved[128]; /* For future expansion. */
+};
+
+
+/*
+ * Because of legacy interfaces, "runlevel" and "sleeptime"
+ * aren't in a seperate struct in the union.
+ *
+ * The weird sizes are because init expects the whole
+ * struct to be 384 bytes.
+ */
+struct init_request {
+ int magic; /* Magic number */
+ int cmd; /* What kind of request */
+ int runlevel; /* Runlevel to change to */
+ int sleeptime; /* Time between TERM and KILL */
+ union {
+ struct init_request_bsd bsd;
+ char data[368];
+ } i;
+};
+
+#endif
diff --git a/inittab.c b/inittab.c
new file mode 100644
index 0000000..0e18e5f
--- /dev/null
+++ b/inittab.c
@@ -0,0 +1,374 @@
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <errno.h>
+#include "ninitfeatures.h"
+#include "initreq.h"
+#include "djb/buffer.h"
+
+#define out(...) err_b(buffer_out,__VA_ARGS__,(char*)0);
+#define outs(X) buffer_puts(buffer_out, X)
+#define outc(X) buffer_putc(buffer_out, X)
+
+char buffer_out_space[4096];
+buffer b_out = BUFFER_INIT(write, -1, buffer_out_space, 4096);
+
+buffer *buffer_out = &b_out;
+
+struct ientry {
+ char *id;
+ char *level;
+ char *action;
+ char **argv;
+};
+
+char *home, *initscript=0, *app_f=0;
+int fd;
+
+void step(char *s)
+ { out("test -d ",s," || mkdir ",s," ; cd ",s," || ops ",s); }
+
+void out_octal(unsigned char x) {
+ outc('\\'); outc('0' + (x>>6));
+ outc('0' + ((x>>3)&7)); outc('0' + (x&7));
+}
+
+void out_txt(char *s) {
+ unsigned char x;
+ while ((x=*s++)) {
+ if (('a' <= x && x<= 'z') ||
+ ('A' <= x && x<= 'Z') || x==' ' || x=='/') outc(x);
+ else out_octal(x);
+ }
+}
+
+void append(char *m) {
+ outs("printf \"");
+ out_txt(m);
+ outs("\\012\"");
+ if (app_f)
+ { out(" >> ",app_f); }
+}
+
+void rm_all() { out("rm_old"); }
+
+void stop_respawn() {
+ app_f = "setup";
+ append("#!/bin/sh\n"
+ "all=`/bin/nsvc -L`\n"
+ "/bin/nsvc -C0 $all\n"
+ "exec /bin/nsvc -r $all");
+ out("chmod 755 setup ; rm -f sync");
+}
+
+void mk_run(char **xx) {
+ out("ln -s ",*xx++," run");
+ app_f = 0;
+ if (*xx==0) return;
+ outs("printf \"");
+ while (*xx) { out_txt(*xx++); out_txt("\n"); }
+ outs("\" > params\n");
+}
+
+void check_file(char *mode, char *file)
+{ out("test ",mode," ",file," || echo '*** Missing file:' ",home,"/",file);}
+
+void check_utmp(char *utmp_file) {
+ out("if test ! -f ",utmp_file," ; then");
+ out(" echo '*** WARNING *** Missing file:' ", utmp_file);
+ out(" echo 'ninit stores in last file the variable INIT_RUNLEVEL'");
+ out(" echo 'ninit does not use this variable. Some other programs'");
+ out(" echo 'can use it (scripts from /etc/init.d/). If you have'");
+ out(" echo 'some troubles, create this file latter!'");
+ out("fi\n");
+}
+
+void die_mem() { die(111,"Out of memory"); }
+
+int main(int Argc, char **Argv) {
+ static char *initdefault;
+ int seen_pf=-1, seen_pw=-1, seen_pn=-1, seen_po=-1;
+ int seen_cad=-1, seen_kbr=-1;
+ int seen_l0=-1, seen_l6=-1;
+ char *p,*s,*service,**argv;
+ int len,argc,k,j;
+ struct ientry *qq,ee;
+
+ s = Argv[1];
+ if (s && s[0]=='-' && s[1]=='I' && s[2]) {
+ initscript = s+2;
+ Argv++; Argc--;
+ }
+
+ if (Argc<4 || Argv[2][0] == 0)
+ { die(100,"usage: ninit-inittab [-I/etc/initscript] /etc/inittab "INITROOT" output_file"); }
+ home = Argv[2];
+
+ if ((fd = open(Argv[1], O_RDONLY)) <0)
+ { die(100, "error opening: ",Argv[1]); }
+
+ if (GLOBAL_READ(fd,s, len,128000))
+ { die(1, "error reading: ", Argv[1]); }
+ close(fd);
+ s[len]=0;
+
+ if ((fd = open(Argv[3], O_CREAT|O_TRUNC|O_WRONLY, 0755)) <0)
+ { die(100, "error opening: ",Argv[3]); }
+ buffer_out->fd = fd;
+
+ argc = splitmem(0,s,'\n');
+ argv=alloca((len+1) * sizeof(char*)); if (argv==0) die_mem();
+ splitmem(argv,s,'\n');
+
+ for (len=0,k=0; argv[k]; k++) {
+ s=argv[k];
+ if (*s=='#' || *s==0) continue;
+ ++len;
+ }
+
+ qq = alloca((len+2)*sizeof(struct ientry)); if (qq==0) die_mem();
+
+ msg("\tvalid ",Argv[1]," entries");
+ for (len=0,k=0; argv[k]; k++) {
+ char **zz;
+ s=argv[k];
+ if (*s == '#' || *s==0) continue;
+ byte_zero(&ee, sizeof(struct ientry));
+
+ ee.id = s;
+ s += str_chr(s,':'); if (*s==0) continue; *s++ = 0;
+ ee.level = s;
+ s += str_chr(s,':'); if (*s==0) continue; *s++ = 0;
+ ee.action = s;
+ s += str_chr(s,':'); if (*s==0) continue; *s++ = 0;
+
+ msg(ee.id,":",ee.level,":",ee.action,":",s);
+ if (!str_diff(ee.action, "initdefault"))
+ initdefault = ee.level;
+ if (*s==0) continue;
+
+ zz = alloca(40 * sizeof(char*)); if (zz==0) die_mem();
+ zz += 40;
+ *--zz = 0;
+
+ /* See if there is an "initscript" (except in single user mode). */
+ if (initscript && str_diff("S",ee.level)) {
+ /* Build command line using "initscript" */
+ *--zz = s;
+ *--zz = ee.action;
+ *--zz = ee.level;
+ *--zz = ee.id;
+ *--zz = initscript;
+ *--zz = "/bin/sh";
+ ee.argv = zz;
+ } else if (strpbrk(s, "~`!$^&*()=|\\{}[];\"'<>?")) {
+ /* See if we need to fire off a shell for this command */
+ *--zz = s;
+ *--zz = "-c";
+ *--zz = "/bin/sh";
+ ee.argv = zz;
+ } else {
+ /* Split up command line arguments */
+ int f;
+ zz -= 22;
+ ee.argv = zz;
+ for (f = 0; f < 20; f++) {
+ while(*s == ' ' || *s == '\t') s++;
+ zz[f] = s;
+
+ if (*s == 0) break;
+ while (*s && *s != ' ' && *s != '\t' && *s != '#') s++;
+
+ if (*s == '#' || *s == 0) {
+ f++;
+ *s = 0;
+ break;
+ }
+ *s++ = 0;
+ }
+ zz[f] = 0;
+ }
+
+ byte_copy(&qq[len], sizeof(ee), &ee);
+ ++len;
+ }
+ for (k=0; k<len; k++)
+ for (j=k+1; j<len; j++)
+ if (!str_diff(qq[k].id, qq[j].id)) {
+ msg(" \a*** WARNING *** ", Argv[1],": duplicated id: ",qq[k].id);
+ }
+
+ if (initdefault==0) { die(100, "unable to find initdefault level"); }
+
+ /* -------------- change bellow this line ---------------- */
+ out("#!/bin/sh\n"
+ "# WARNING: This file was auto-generated.\n"
+ "# Source: ",Argv[1],
+ "\n\nexport PATH=/bin:/usr/bin\n"
+ "umask 022\n"
+ "ops () { echo cd error $1; exit 1; }\n"
+ "rm_old () { rm -f run sync setup params respawn; }\n");
+
+ check_utmp("/var/run/utmp");
+
+ step(home);
+ check_file("-d","sys");
+ check_file("-x","sys/run");
+ check_file("-x","sys/run-wait");
+ check_file("-x","sys/update");
+ check_file("-x","sys/procfs");
+ check_file("-x","sys/remove");
+ check_file("-p","in");
+ check_file("-p","out");
+
+ out("\nif test -f .services_ready; then\n"
+ " echo Please read ",home,"/.services_ready\n"
+ " exit 1\nfi\n");
+
+ out("test -d default || mkdir default\n"
+ "test -f default/depends && "
+ "mv -f default/depends default/depends.old\n"
+ "rm -f default/depends\n");
+
+ step("sysvinit");
+ rm_all();
+ out("ln -sf /sbin/ninit-sysvinit run");
+ app_f = "sysvinit-timeout";
+ append("90:0\n"
+ "DO NOT remove or rename this file\n"
+ "unless you know what are you doing!");
+ out("cd ..\n");
+
+ step("powerS");
+ rm_all();
+ out("ln -sf /sbin/ninit-sysvinit run\n"
+ "echo powerS > params");
+ out("cd ..\n");
+
+ step("update");
+ rm_all();
+ out("ln -sf /sbin/ninit-reload run");
+ app_f = "params";
+ append("-v\n-Rupdate\n-a30\n-u\n/sbin/ninit");
+ out("cd ..\n",
+ "ln -sf update level","U\n",
+ "ln -sf update level","Q\n");
+
+ step("ngetty");
+ rm_all();
+ out("ln -s /sbin/ngetty run");
+ app_f = "params";
+ append("1\n2\n3\n4\n5\n6");
+ out("> respawn");
+ app_f = "environ";
+ append("\nTERM=linux");
+ out("cd ..\n");
+
+
+ for (k=0; k<len; k++) {
+ char **xx;
+ byte_copy(&ee, sizeof(ee), &qq[k]);
+ xx=ee.argv;
+ if (!str_diff(ee.action, "off")) {
+ msg("\tskipping entry\t", ee.id,":",ee.level,":",ee.action,": ...");
+ continue;
+ }
+
+ service = alloca(4+str_len(ee.id)); if (service==0) die_mem();
+ service[0]='_';
+ str_copy(service+1,ee.id);
+
+ if (!str_diff(ee.action, "ctrlaltdel")) { seen_cad = k; }
+ else if (!str_diff(ee.action, "kbrequest")) { seen_kbr = k; }
+ else if (!str_diff(ee.action, "sysinit") ||
+ !str_diff(ee.action, "boot") ||
+ !str_diff(ee.action, "bootwait") ||
+ !str_diff(ee.action, "once") ||
+ !str_diff(ee.action, "powerfail") ||
+ !str_diff(ee.action, "powerwait") ||
+ !str_diff(ee.action, "powerfailnow") ||
+ !str_diff(ee.action, "powerokwait")) {
+ step(service);
+ rm_all();
+ if (ee.action[0] == 's' || ee.action[4] == 'w') out("> sync");
+ mk_run(xx);
+ out("cd ..\n");
+ if (ee.action[0] != 'p') {
+ app_f = "default/depends\n";
+ append(service);
+ }
+ if (!str_diff(ee.action, "powerfail")) { seen_pf = k; }
+ if (!str_diff(ee.action, "powerwait")) { seen_pw = k;}
+ if (!str_diff(ee.action, "powerfailnow")) { seen_pn = k; }
+ if (!str_diff(ee.action, "powerokwait")) { seen_po = k; }
+ }
+ else {
+ step(service);
+ rm_all();
+ if (!str_diff(ee.action, "wait")) {
+ p = ee.level;
+ out("> sync");
+ if (p[0] && p[1]==0) {
+ if (*p=='0') { seen_l0=k; stop_respawn(); }
+ if (*p=='6') { seen_l6=k; stop_respawn(); }
+ *--xx = p;
+ *--xx = "/sbin/ninit-runlevel";
+ }
+ }
+ if (!str_diff(ee.action,"respawn")) {
+ out("> respawn");
+ if (strstr(ee.argv[0],"getty")) {
+ *--xx = ee.id;
+ *--xx = "/sbin/pututmpid";
+ }
+ }
+ mk_run(xx);
+ out("cd ..\n");
+
+ if (!str_diff(ee.action, "wait")) {
+ p = ee.level;
+ if (p[0] && p[1]==0) out("ln -s ",service," level",p, "\n");
+ }
+
+ s = ee.level;
+ s += str_chr(s, *initdefault);
+ if (*s) {
+ app_f = "default/depends\n";
+ append(service);
+ }
+ }
+ }
+
+ s = "ln -sf _";
+ if (seen_kbr>=0 && seen_l6>=0) out(s,qq[seen_l6].id," kbreq");
+ if (seen_cad>=0 && seen_l0>=0) out(s,qq[seen_l0].id," ctrlaltdel");
+
+ if (seen_l0>=0) out(s,qq[seen_l0].id," halt");
+ if (seen_l6>=0) out(s,qq[seen_l6].id," reboot");
+
+ /* XXX powerfail or powerwait ??? ; first may be ;-) */
+ if (seen_pf>=0 && seen_pf < seen_pw) out(s,qq[seen_pf].id," powerF");
+ if (seen_pw>=0 && seen_pw < seen_pf) out(s,qq[seen_pw].id," powerF");
+
+ if (seen_pn>=0) out(s,qq[seen_pn].id," powerL");
+ if (seen_po>=0) out(s,qq[seen_po].id," powerO");
+ out("");
+
+ out("test -d _l0 && echo INIT_HALT=POWERDOWN > _l0/environ\n"
+ "if test -d _l6 ; then");
+ app_f = "_l6/environ";
+ append("#INIT_HALT=POWERDOWN\n"
+ "#INIT_HALT=HALT\n"
+ "INIT_HALT");
+ out("fi\n");
+
+ app_f = ".services_ready";
+ append("*** WARNING ***\n"
+ "Someone made services in this directory.\n"
+ "If you want to overwrite them remove me.\n");
+
+ buffer_flush(buffer_out);
+
+ close(fd);
+ return 0;
+}
diff --git a/install-bin.c b/install-bin.c
new file mode 100644
index 0000000..fc18b07
--- /dev/null
+++ b/install-bin.c
@@ -0,0 +1,129 @@
+#include <unistd.h>
+#include <fcntl.h>
+#include <alloca.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include "ninitfeatures.h"
+#include "error_table.h"
+#include "uid.h"
+
+#define U "unable to "
+
+static char *e() { return error_string(table, errno); }
+static void nomem() { die(111,"out of memory"); }
+static void ex(char *s0, char *s1) { die(111,U,s0,": ",s1,": ",e()); }
+
+static void doit(char *to, char *line) {
+ char *x, **arg, *target, *tmp;
+ char *type, *mid, *name, *alias;
+ unsigned long uid, gid, mode;
+ int in, out, len;
+ static char *uid_global, *gid_global, *mode_global, *mid_global, V;
+
+ len=splitmem(0,line,':'); if (len <7) return;
+ arg=alloca((len+1) * sizeof(char*)); if (arg==0) nomem();
+ splitmem(arg,line,':');
+
+ type = arg[0];
+ x=arg[1]; if (!*x==0 && uid_global) x=uid_global;
+ if (*x) uid=atoulong(x); else uid = -1;
+
+ x=arg[2]; if (!*x==0 && gid_global) x=gid_global;
+ if (*x) gid=atoulong(x); else gid = -1;
+
+ x=arg[3]; if (!*x && mode_global) x=mode_global; scan_8ulong(x,&mode);
+ mid = arg[4]; if (!*mid && mid_global) mid=mid_global;
+ name= arg[5];
+ x=arg[6]; alias = (*x) ? x : name;
+
+ len = str_len(to) + str_len(mid) + str_len(name);
+ x=alloca(2*len + 32); if (x==0) nomem();
+ target = x;
+ x += str_copy(x,to);
+ x += str_copy(x,mid);
+ x += str_copy(x,name);
+ while (target[0]=='/' && target[1]=='/') target++;
+ tmp = (*type=='x') ? x+2 : 0;
+
+ switch(*type) {
+ case 'p':
+ if (SYS_mknod(target,S_IFIFO|0600,0) == -1)
+ if (errno != EEXIST) ex("mknod",target);
+ if (V) msg("pipe:\t", target);
+ break;
+
+ case 'd':
+ if (mkdir(target,0700) == -1)
+ if (errno != EEXIST) ex("mkdir",target);
+ if (V) msg("mkdir:\t", target);
+ break;
+
+ case 'c':
+ case 'x':
+ if ((in=open(alias, O_RDONLY)) <0) ex("open",alias);
+
+ if (*type == 'c') out = open(target, O_WRONLY|O_CREAT|O_TRUNC, 0600);
+ else out = open_tmpfd(target, tmp, 0600);
+ if (out <0) ex("open",target);
+
+ x=alloca(8192); if (x==0) nomem();
+ for (;;) {
+ len=read(in,x,8192);
+ if (len==0) break;
+ else if (len==-1) ex("read",alias);
+ else if (len != write(out,x,len)) { die(111,U,"write",target); }
+ }
+
+ close(in);
+ if (fsync(out)) ex("fsync",target);
+ if (close(out)) ex("close",target);
+ if (tmp && rename(tmp,target))
+ { die(111,U,"rename: ", tmp, " -> ", target, ": ", e()); }
+ if (V) msg(alias, "\t-> ", target);
+ break;
+
+ case 'g':
+ x=arg[1]; uid_global = (*x) ? x : 0;
+ x=arg[2]; gid_global = (*x) ? x : 0;
+ x=arg[3]; mode_global = (*x) ? x : 0;
+ x=arg[4]; mid_global = (*x) ? x : 0;
+ return;
+
+ case 'v':
+ if (arg[1][0]) V=1;
+ else V=0;
+
+ default:
+ return;
+ }
+ if (SYS_chown(target,uid,gid) <0) ex("chown",target);
+ if (chmod(target,mode) <0) ex("chmod",target);
+}
+
+int main(int argc, char **argv) {
+ char *to, **arg, *s=0;
+ if (argc<2) { die(100,"usage: install-bin Dir < File\n "
+ "install-bin Dir c:::755:mid:file:: x:::755:mid:name:source: ...\n"
+ "File contains lines:\ntype:uid:gid:mode:middle:target:source:\n"
+ "type is one of the letters: vpdcxg\n"
+ "type g sets global uid:gid:mode:middle\n"); }
+
+ to = argv[1];
+ errmsg_iam("install-bin");
+ umask(077);
+
+ if (argc == 2) {
+ int len;
+ if (GLOBAL_READ(0,s, len,100000)) ex("read","stdin");
+ close(0);
+ s[len]=0;
+
+ len = splitmem(0,s,'\n');
+ arg = alloca((len+1) * sizeof(char*)); if (arg==0) nomem();
+ splitmem(arg,s,'\n');
+ } else
+ arg = argv+2;
+
+ for (; (s=*arg); ++arg) doit(to, s);
+ return 0;
+}
diff --git a/lib/do_wtmp.c b/lib/do_wtmp.c
new file mode 100644
index 0000000..1799895
--- /dev/null
+++ b/lib/do_wtmp.c
@@ -0,0 +1,11 @@
+#include <unistd.h>
+#include <fcntl.h>
+#include <utmp.h>
+
+void do_wtmp(struct utmp *utmp) /*EXTRACT_INCL*/ {
+ int fd;
+ if ((fd=open(_PATH_WTMP, O_WRONLY | O_APPEND)) >= 0) {
+ write(fd, utmp, sizeof(struct utmp));
+ close(fd);
+ }
+}
diff --git a/lib/err.c b/lib/err.c
new file mode 100644
index 0000000..fd4218e
--- /dev/null
+++ b/lib/err.c
@@ -0,0 +1,39 @@
+#include <stdarg.h>
+#include "../ninitfeatures.h"
+#include "../djb/buffer.h"
+
+#if 0
+void err(int fd, const char *m, ...) /*EXTRACT_INCL*/
+#endif
+
+#ifdef ERRMSG_BUFFER
+#define P(S) buffer_puts(X,S)
+#define E() err_b(buffer *X, const char *m, ...)
+
+#else
+#define P(S) errmsg_puts(X,S)
+#define E() err(int X, const char *m, ...)
+#endif
+
+extern const char *errmsg_argv0;
+
+void E() {
+ const char *s=m;
+ va_list a;
+ va_start(a,m);
+
+ if (errmsg_argv0) {
+ P(errmsg_argv0);
+ P(": ");
+ }
+
+ while (s) {
+ P(s);
+ s=va_arg(a,const char*);
+ }
+ P("\n");
+#ifndef ERRMSG_BUFFER
+ P(0);
+#endif
+ va_end(a);
+}
diff --git a/lib/err_b.c b/lib/err_b.c
new file mode 100644
index 0000000..2de7017
--- /dev/null
+++ b/lib/err_b.c
@@ -0,0 +1,5 @@
+#define ERRMSG_BUFFER
+#include "err.c"
+#if 0
+void err_b(buffer *b, const char *m, ...) /*EXTRACT_INCL*/
+#endif
diff --git a/lib/errmsg_argv0.c b/lib/errmsg_argv0.c
new file mode 100644
index 0000000..ef63ea9
--- /dev/null
+++ b/lib/errmsg_argv0.c
@@ -0,0 +1 @@
+const char *errmsg_argv0=0;
diff --git a/lib/errmsg_put.c b/lib/errmsg_put.c
new file mode 100644
index 0000000..9afe0ce
--- /dev/null
+++ b/lib/errmsg_put.c
@@ -0,0 +1,20 @@
+#include <sys/uio.h>
+#include "../ninitfeatures.h"
+
+#ifndef ERRMSG_PUTS_LEN
+#define ERRMSG_PUTS_LEN 15
+#endif
+
+void errmsg_put(int fd, const char *buf, unsigned int len) /*EXTRACT_INCL*/ {
+ static struct iovec errmsg_iov[ERRMSG_PUTS_LEN];
+ static int k;
+ if (buf==0 || k==ERRMSG_PUTS_LEN) {
+ if (fd>=0) writev(fd,errmsg_iov,k);
+ k = 0;
+ }
+ if (buf && len) {
+ errmsg_iov[k].iov_base = (char *)buf;
+ errmsg_iov[k].iov_len = len;
+ k++;
+ }
+}
diff --git a/lib/errmsg_puts.c b/lib/errmsg_puts.c
new file mode 100644
index 0000000..41c3b88
--- /dev/null
+++ b/lib/errmsg_puts.c
@@ -0,0 +1,5 @@
+#include "../ninitfeatures.h"
+void errmsg_puts(int fd, const char *buf) /*EXTRACT_INCL*/ {
+ if (buf==0) { errmsg_put(fd, buf, 0); return; }
+ if (buf[0]) errmsg_put(fd, buf, str_len(buf));
+}
diff --git a/lib/error_string.c b/lib/error_string.c
new file mode 100644
index 0000000..d7fd210
--- /dev/null
+++ b/lib/error_string.c
@@ -0,0 +1,16 @@
+#include "../ninitfeatures.h"
+#if 0
+struct error_table { int n; char *s; }; /*EXTRACT_UNMOD*/
+#endif
+
+char *error_string(struct error_table *table, int n) /*EXTRACT_INCL*/ {
+ static char y[28];
+ char *x=y;
+ for (; table->s; table++)
+ if (table->n == n) return table->s;
+
+ x += str_copy(x,"error=");
+ x += fmt_ulong(x,n);
+ *x = 0;
+ return y;
+}
diff --git a/lib/fu.c b/lib/fu.c
new file mode 100644
index 0000000..074007d
--- /dev/null
+++ b/lib/fu.c
@@ -0,0 +1,16 @@
+#include "../int_defs.h"
+
+static char fu_buffer[7*12];
+/* format up to 6 numbers */
+
+char *fu(uint32t u) /*EXTRACT_INCL*/ {
+ static char k=7;
+ char *p = fu_buffer + 12*k;
+ *--p = 0;
+ do {
+ *--p = '0' + (u%10);
+ u /= 10;
+ } while (u);
+ if (--k == 1) k=7;
+ return p;
+}
diff --git a/lib/nano_sleep.c b/lib/nano_sleep.c
new file mode 100644
index 0000000..6da74a0
--- /dev/null
+++ b/lib/nano_sleep.c
@@ -0,0 +1,6 @@
+#include "../int_defs.h"
+#include <time.h>
+void nano_sleep(uint32t sec, uint32t nsec) /*EXTRACT_INCL*/ {
+ struct timespec ts = { sec, nsec };
+ nanosleep(&ts, 0);
+}
diff --git a/lib/open_tmpfd.c b/lib/open_tmpfd.c
new file mode 100644
index 0000000..e94bf32
--- /dev/null
+++ b/lib/open_tmpfd.c
@@ -0,0 +1,18 @@
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include "../ninitfeatures.h"
+
+int open_tmpfd(char *target, char *tmp, int mode) /*EXTRACT_INCL*/ {
+ unsigned long len;
+ char *x;
+ int fd;
+ for (len=0 ;; len++) {
+ x = tmp + str_copy(tmp, target);
+ if (len) x += fmt_ulong(x,len);
+ x[0] = '~'; x[1] = 0;
+ fd = open(tmp, O_WRONLY|O_CREAT|O_EXCL, mode);
+ if (fd >=0 || errno != EEXIST) break;
+ }
+ return fd;
+}
diff --git a/lib/pathexec_run.c b/lib/pathexec_run.c
new file mode 100644
index 0000000..2e369e1
--- /dev/null
+++ b/lib/pathexec_run.c
@@ -0,0 +1,43 @@
+/* again DJB; see http://cr.yp.to */
+#include <unistd.h>
+#include <errno.h>
+#include <alloca.h>
+#include <stdlib.h>
+
+#include "../ninitfeatures.h"
+
+void pathexec_run(char *file,char **argv,char **envp) /*EXTRACT_INCL*/ {
+ char *path,*tmp;
+ int savederrno=0;
+ unsigned int len, next, file_len;
+
+ if (file[str_chr(file,'/')]) { execve(file,argv,envp); return; }
+ file_len = str_len(file) + 1;
+
+ path = env_get("PATH");
+ if (!path) path = "/bin:/usr/bin";
+ tmp = alloca(4 + str_len(path) + file_len);
+ if (!tmp) { errno=ENOMEM; return; }
+
+ for (;;) {
+ next = str_chr(path,':');
+ len = next;
+
+ if (next==0) { tmp[0] = '.'; len = 1; }
+ else { byte_copy(tmp,next,path); }
+ tmp[len] = '/';
+ byte_copy(tmp+len+1, file_len, file);
+
+ execve(tmp,argv,envp);
+ if (errno != ENOENT) {
+ savederrno = errno;
+ if ((errno != EACCES) && (errno != ENOEXEC) && (errno != ENOTDIR)) return;
+ }
+
+ if (path[next]==0) {
+ if (savederrno) errno = savederrno;
+ return;
+ }
+ path += (next+1);
+ }
+}
diff --git a/lib/read_header.c b/lib/read_header.c
new file mode 100644
index 0000000..4a78b23
--- /dev/null
+++ b/lib/read_header.c
@@ -0,0 +1,18 @@
+#include <unistd.h>
+#include <fcntl.h>
+
+#define MEM_BUF 160
+
+char *read_header(const char *name) /*EXTRACT_INCL*/ {
+ static char buf[MEM_BUF+1];
+ int fd, k;
+
+ fd =open(name, O_RDONLY);
+ if (fd < 0) return 0;
+ k =read(fd, buf, MEM_BUF);
+ close (fd);
+ if (k < 0) return 0;
+
+ buf[k] = 0;
+ return buf;
+}
diff --git a/lib/read_ulongs.c b/lib/read_ulongs.c
new file mode 100644
index 0000000..90720ff
--- /dev/null
+++ b/lib/read_ulongs.c
@@ -0,0 +1,8 @@
+#include "../ninitfeatures.h"
+
+int read_ulongs(char *name, unsigned long *u, int len) /*EXTRACT_INCL*/ {
+ char *x = read_header(name);
+ int dummy;
+ if (x==0) return 0;
+ return scan_ulongs(x, u, len, scan_ulong, ':', &dummy);
+}
diff --git a/lib/scan_sec.c b/lib/scan_sec.c
new file mode 100644
index 0000000..6377399
--- /dev/null
+++ b/lib/scan_sec.c
@@ -0,0 +1,23 @@
+#include "../ninitfeatures.h"
+
+unsigned int scan_sec(const char *src, unsigned long *ul) /*EXTRACT_INCL*/ {
+ unsigned long tmp, u=0;
+ char ch, *s = (char *)src;
+ for (; *s; ) {
+ s += scan_ulong(s, &tmp);
+ ch = *s;
+ if (ch > 96) ch -= 32; /* upper case */
+
+ switch (ch) {
+ case 'W': tmp *= 10080; s++; break;
+ case 'D': tmp *= 1440; s++; break;
+ case 'H': tmp *= 60; s++; break;
+ case 0: break;
+ default: u += tmp; goto ready;
+ }
+ u += tmp;
+ }
+ ready:
+ *ul = u * 60;
+ return s-src;
+}
diff --git a/lib/scan_ulongs.c b/lib/scan_ulongs.c
new file mode 100644
index 0000000..db5631f
--- /dev/null
+++ b/lib/scan_ulongs.c
@@ -0,0 +1,17 @@
+#include "../ninitfeatures.h"
+
+unsigned int scan_ulongs(char *src, unsigned long *u, int len, unsigned int (*op)(), char sep, int *read_len) /*EXTRACT_INCL*/ {
+ int j, k;
+ char *p=src;
+
+ for (k=0; k<len;) {
+ j = op(p, u+k); if (j==0) break;
+ ++k;
+ p += j;
+ if (*p != sep) break;
+ ++p;
+ }
+
+ *read_len = (p-src);
+ return k;
+}
diff --git a/lib/skip_comments.c b/lib/skip_comments.c
new file mode 100644
index 0000000..fd0bdde
--- /dev/null
+++ b/lib/skip_comments.c
@@ -0,0 +1,11 @@
+void skip_comments(char **s) /*EXTRACT_INCL*/ {
+ char **d, *p;
+ for (d=s; (p=*s); s++) {
+ if (p[0] == '#') {
+ if (p[1] == '#') ++p;
+ else continue;
+ }
+ *d++ = p;
+ }
+ *d=0;
+}
diff --git a/lib/splitmem.c b/lib/splitmem.c
new file mode 100644
index 0000000..f5d13aa
--- /dev/null
+++ b/lib/splitmem.c
@@ -0,0 +1,18 @@
+unsigned int splitmem(char **v, char *s, char c) /*EXTRACT_INCL*/ {
+ if (v) {
+ char **w=v;
+ *w++=s;
+ for (;;) {
+ while (*s && *s!=c) s++;
+ if (*s==0) break;
+ *s=0;
+ *w++ = ++s;
+ }
+ *w=0;
+ return (w-v);
+ } else {
+ unsigned int n=1;
+ for (; *s; s++) if (*s==c) n++;
+ return n;
+ }
+}
diff --git a/lib/strpbrk.c b/lib/strpbrk.c
new file mode 100644
index 0000000..9f39fc3
--- /dev/null
+++ b/lib/strpbrk.c
@@ -0,0 +1,10 @@
+#include "../ninitfeatures.h"
+
+char *strpbrk(const char *s, const char *accept) /*EXTRACT_INCL*/ {
+ register int i,l=str_len(accept);
+ for (; *s; s++)
+ for (i=0; i<l; i++)
+ if (*s == accept[i])
+ return (char*)s;
+ return 0;
+}
diff --git a/lib/strstr.c b/lib/strstr.c
new file mode 100644
index 0000000..39e30e5
--- /dev/null
+++ b/lib/strstr.c
@@ -0,0 +1,16 @@
+#include "../ninitfeatures.h"
+
+char *strstr(const char *haystack, const char *needle) /*EXTRACT_INCL*/ {
+ unsigned int nl=str_len(needle);
+ unsigned int hl=str_len(haystack);
+ int i;
+ if (!nl) goto found;
+
+ for (i=hl-nl+1; i>0; --i) {
+ if (*haystack==*needle && !byte_diff(haystack,nl,needle))
+found:
+ return (char*)haystack;
+ ++haystack;
+ }
+ return 0;
+}
diff --git a/lib/utmp_io.c b/lib/utmp_io.c
new file mode 100644
index 0000000..1d8c423
--- /dev/null
+++ b/lib/utmp_io.c
@@ -0,0 +1,29 @@
+#include <unistd.h>
+#include <fcntl.h>
+#include <utmp.h>
+
+#define UTMP_SIZE (sizeof(struct utmp))
+
+/* type: F_RDLCK or F_WRLCK */
+struct utmp *utmp_io(int fd, struct utmp *ut, int type) /*EXTRACT_INCL*/ {
+ int ret;
+ struct flock fl;
+ int (*op)() = (type==F_WRLCK) ? (int(*)())write : (int(*)())read;
+
+ fl.l_whence = SEEK_CUR;
+ fl.l_start = 0;
+ fl.l_len = UTMP_SIZE;
+ fl.l_pid = 0;
+ fl.l_type = type;
+
+ if (fcntl(fd, F_SETLKW, &fl)) return 0;
+ ret = op(fd, ut, UTMP_SIZE);
+
+ fl.l_start = -UTMP_SIZE;
+ fl.l_type = F_UNLCK;
+
+ fcntl(fd, F_SETLK, &fl);
+
+ if (ret != UTMP_SIZE) return 0;
+ return ut;
+}
diff --git a/lib/x_atoi.c b/lib/x_atoi.c
new file mode 100644
index 0000000..2f6a5fb
--- /dev/null
+++ b/lib/x_atoi.c
@@ -0,0 +1,12 @@
+
+int x_atoi(const char *src) /*EXTRACT_INCL*/ {
+ register const char *s;
+ register long int dest=0;
+ register unsigned char c;
+
+ s=src;
+ if (*s=='-' /* || *s=='+' */) ++s;
+
+ while ((c=*s-'0')<10) { ++s; dest=dest*10 + c; }
+ return (*src=='-') ? -dest : dest;
+}
diff --git a/library_files b/library_files
new file mode 100644
index 0000000..f30c96f
--- /dev/null
+++ b/library_files
@@ -0,0 +1,52 @@
+Z/byte_copy.o
+Z/byte_copyr.o
+Z/byte_diff.o
+Z/byte_set.o
+Z/byte_zero.o
+Z/str_chr.o
+Z/str_copy.o
+Z/str_copyn.o
+Z/str_diff.o
+Z/str_diffn.o
+Z/str_len.o
+Z/str_rchr.o
+Z/atoulong.o
+cron.o
+djb/buffer_get.o
+djb/buffer_put.o
+djb/buffer_putc.o
+djb/buffer_puts.o
+djb/env_get.o
+djb/fmt_ulong.o
+djb/scan_8ulong.o
+djb/scan_ulong.o
+lib/do_wtmp.o
+lib/err.o
+lib/err_b.o
+lib/errmsg_argv0.o
+lib/errmsg_put.o
+lib/errmsg_puts.o
+lib/error_string.o
+lib/fu.o
+lib/nano_sleep.o
+lib/open_tmpfd.o
+lib/pathexec_run.o
+lib/read_header.o
+lib/read_ulongs.o
+lib/scan_sec.o
+lib/scan_ulongs.o
+lib/skip_comments.o
+lib/splitmem.o
+lib/strpbrk.o
+lib/strstr.o
+lib/utmp_io.o
+lib/x_atoi.o
+misc/child_block.o
+misc/dup2_inout.o
+misc/fmt_argv.o
+misc/opendevconsole.o
+misc/set_sigaction.o
+misc/sulogin.o
+misc/system_child_block.o
+misc/system_set_sigaction.o
+softlimit.o
diff --git a/man/bootlog.8 b/man/bootlog.8
new file mode 100644
index 0000000..f963e29
--- /dev/null
+++ b/man/bootlog.8
@@ -0,0 +1,89 @@
+.TH bootlog 8 "Dec 28, 2009"
+.SH NAME
+bootlog \- write stdout/stderr of a program to file
+.SH SYNOPSIS
+.B bootlog
+[-12ctar] logsize logfile program [arguments]
+
+.SH DESCRIPTION
+.B bootlog
+is used to start a program and write stdout/stderr to a disk file.
+This works also before mouning any file systems.
+
+.B bottlog
+is designed to log the output of scripts/services at boot time,
+before mounting any file system. This is typical for
+system init.
+
+.B bootlog
+expects that when the program exits, the
+.I logfile
+is writable!
+
+.SH OPTIONS
+.TP
+\-1
+log only stdout
+.TP
+\-2
+log only stderr; default is to log stdout and stderr
+.TP
+\-c
+create logfile
+.TP
+\-t
+truncate logfile
+.TP
+\-a
+append to logfile
+.TP
+\-r
+replace old
+.I logfile
+with
+.I logfile~
+.TP
+.I logsize
+maximal size of output to be logged
+.TP
+.I logfile
+disk file name
+.TP
+.I program
+the name of the
+.I program
+to start
+.TP
+.I arguments
+additional arguments for
+.I program
+
+.SH WARNING
+Use the option \-c to force creating of the
+.I logfile.
+
+Actually the options \-c, \-a, \-t add O_CREAT, O_APPEND, O_TRUNC
+flags to open(2) for
+.I logfile
+
+.SH EXAMPLE
+bootlog -ctr 120000 /tmp/.sysinit.log /etc/rc.d/rc.sysinit
+
+You will have the files
+.I /tmp/.sysinit.log
+and
+.I /tmp/.sysinit.log~
+
+.SH ENVIRON
+.B bootlog
+uses the variable PATH to start
+.I program
+.br
+If PATH is undefined it uses PATH=/bin:/usr/bin
+
+.SH AUTHOR
+.B bootlog
+is part of ninit package written by Nikola Vladov
+
+.SH "SEE ALSO"
+ninit(8), open(2)
diff --git a/man/inittab.8 b/man/inittab.8
new file mode 100644
index 0000000..bcede1c
--- /dev/null
+++ b/man/inittab.8
@@ -0,0 +1,98 @@
+.TH ninit\-inittab 8 "Dec 28, 2009"
+.SH NAME
+ninit\-inittab \- convert /etc/inittab to service directory
+.SH SYNOPSIS
+.B ninit\-inittab
+.I inittab_file
+.I home_directory
+.I output_script
+
+.SH DESCRIPTION
+.B ninit\-inittab
+creates a script using
+.I /etc/inittab
+file.
+This script can be used to create a service directory easy.
+
+.SH OPTIONS
+.TP
+.I inittab_file
+input source file
+.TP
+.I home_directory
+home directory for
+.B ninit
+.TP
+.I output_script
+name of the output script
+
+.SH USAGE
+.B Case A.
+If you don't have services in
+.I /etc/ninit
+you can start:
+
+.PP
+.RS
+cd /tmp
+.br
+ninit\-inittab
+/etc/inittab /etc/ninit services.sh
+.br
+less services.sh
+.br
+/tmp/services.sh
+.RE
+.PP
+
+Then check what the script has done in
+.I /etc/ninit
+
+.B Case B.
+If you already have a
+.I /etc/ninit
+directory with services,
+and don't want to make any changes there you can type:
+
+.PP
+.RS
+cd /tmp; cp /etc/inittab /tmp; vi inittab
+.br
+ninit\-inittab
+/tmp/inittab /tmp/srv services.sh
+.br
+less services.sh
+.br
+/tmp/services.sh
+.RE
+.PP
+
+Then check what the script has done in
+.I /tmp/srv.
+If you find something
+interesting there you can copy it to
+.I /etc/ninit
+
+.SH AUTHOR
+.B ninit\-inittab
+was written by Nikola Vladov.
+
+The aim was to easily convert a host running
+SysVinit to ninit. In my opinion creating services is very easy.
+Maybe you have to look
+in some already prepared service directory first.
+
+Ask google for "ninit archlinux" and you will find
+an excellent git repository with services for
+.I /etc/ninit
+
+
+.SH FILES
+.I /etc/inittab
+.br
+.I /etc/ninit/
+
+.SH "SEE ALSO"
+ninit(8), ninit\-runlevel(8), ninit\-sysvinit(8), pututmpid(8)
+.br
+init(8), inittab(5), runlevel(8)
diff --git a/man/ninit.8 b/man/ninit.8
new file mode 100644
index 0000000..e6a9e8a
--- /dev/null
+++ b/man/ninit.8
@@ -0,0 +1,178 @@
+.TH ninit 8 "Jan 15, 2010"
+.SH NAME
+ninit \- a UNIX process no 1
+.SH SYNOPSIS
+.B ninit
+[\-Mnumber] [\-Hhome] [\-Ssystem] [service] ...
+
+.SH DESCRIPTION
+.B ninit
+is a possible first process the kernel starts. It can
+start/stop/monitor all services the OS needs.
+
+To use ninit as system init, first read available documentation in
+.I http://riemann.fmi.uni-sofia.bg/ninit/
+and then add the parameter
+\fIinit=/sbin/ninit\fR
+to your kernel command line.
+
+If you want to use ninit
+only to start/monitor services and continue to use
+the default init(8) then put in
+.I /etc/inittab
+.br
+.B NI:12345:respawn:/sbin/ninit
+.br
+In this case you should remove the service
+.B sysvinit
+and don't use the programs:
+ninit\-runlevel(8), ninit\-sysvinit(8).
+Similarly it is possible to start ninit using
+.B /etc/init.d/ninit
+script.
+
+
+.SH USAGE
+.B ninit
+will by default do nothing except start the ninit
+service defined in
+.I /etc/ninit/default
+which usually contains a file named
+.I depends
+specifying which services are to be started at bootup.
+
+To control ninit use the companion program
+.B nsvc.
+Do not make it SUID unless you know what are you doing!
+
+The program
+.B ninit\-sysvinit
+listens to the fifo
+.I /dev/initctl
+and allows SysVinit programs
+shutdown, halt, reboot, telinit to work properly.
+If there is a service
+.I /etc/ninit/sysvinit
+it starts
+.B ninit\-sysvinit.
+
+
+To stop the box running
+.B ninit
+use the programs
+.B ninit\-shutdown
+or
+.B nsvc.
+
+.SH "NONROOT USAGE"
+Any nonroot user can use
+.B /sbin/ninit
+to start/monitor own services. First make private
+ninit directory with
+.br
+.B /etc/ninit/bin/ninit\-conf ~/.ninit
+.br
+and put somewhere in shell profile:
+.B export NINIT_HOME=~/.ninit
+
+Then create some services in $NINIT_HOME and start
+.br
+.B /sbin/ninit -H$NINIT_HOME
+
+.SH OPTIONS
+.TP
+.B \-Mnumber
+Tells ninit to use
+.B number
+bytes for memory buffer. One service uses approximately 30 bytes.
+.TP
+.B \-Hhome
+Changes the ninit home. Default:
+.I /etc/ninit
+.TP
+.B \-Ssystem
+Changes the ninit system directory. Default:
+.I sys
+
+.SH SIGNALS
+On receiving of some signals
+.B ninit
+starts appropiate service.
+
+.TP
+.B SIGINT
+Normally the kernel sends this signal to ninit when CTRL-ALT-DEL is
+pressed. It activates the \fIctrlaltdel\fP action and ninit
+starts the service
+.I ctrlaltdel
+.TP
+.B SIGWINCH
+The kernel sends this signal when the \fIKeyboardSignal\fP key is hit.
+It activates the \fIkbrequest\fP action
+and ninit starts the service
+.I kbreq
+.TP
+.B SIGHUP
+Has the same effect as telinit q.
+Ninit restarts the service
+.I levelQ
+.TP
+.B SIGUSR1
+On receipt of this signal, ninit closes and re-opens its control fifo,
+.I /dev/initctl.
+Useful for bootscripts when /dev is remounted.
+.TP
+.B SIGPWR
+Ninit starts the service
+.I powerS.
+This service starts the program
+.B ninit-sysvinit
+with one argument: powerS.
+
+.SH SERVICES
+Each service has own subdirectory in
+.I /etc/ninit/
+three. There are lots of config files for a service.
+The main daemon
+.B ninit
+check only the files
+.I depends, sync, respawn, end
+to start a service.
+Check olso the link:
+.br
+.I http://riemann.fmi.uni-sofia.bg/ninit/files.html
+
+If the servive name start with '#' or '\-' it is blacklisted.
+You can now blacklist services temporarily by passing it on
+the kernel command line. The first time they are to be started
+will then be skipped. Use this, for example, to not start the dhcp
+client when your notebook is not plugged in a network.
+Look in ninitfeatures.h if ninit is compiled to use this option.
+
+.SH FILES
+.I NINIT_HOME/in
+.br
+.I NINIT_HOME/out
+.br
+.I NINIT_HOME/sys/
+.br
+.I /dev/initctl
+
+
+.SH AUTHOR
+ninit was written by Nikola Vladov and can be downloaded from
+.I http://riemann.fmi.uni-sofia.bg/ninit/
+
+.SH SEE ALSO
+nsvc(8), ninit-service(8), ninit-reload(8)
+.br
+ninit-shutdown(8), ninit-reboot(8), nkillall(8)
+.br
+ninit-sysvinit(8), ninit-runlevel(8)
+.br
+minit(8),
+.I http://www.fefe.de/minit/
+.br
+init(8), shutdown(8), halt(8), reboot(8)
+
+Part of this manpage was written by Erich Schubert.
diff --git a/man/nkillall.8 b/man/nkillall.8
new file mode 100644
index 0000000..6564834
--- /dev/null
+++ b/man/nkillall.8
@@ -0,0 +1,139 @@
+.TH nkillall 8 "Dec 28, 2009"
+.SH NAME
+nkillall \- send signals to all processes; write messages to users
+.SH SYNOPSIS
+.B nkillall
+-[vq] [-s secs] [-M|W mesg] [-signum] [-E prog [arg[s]]
+.SH DESCRIPTION
+.B nkillall
+sends signals
+to all processes skipping it's own PID.
+It is a simple program that does not use \fI/proc\fR FS.
+It uses kill(2) and execve(2) syscalls. It is designed as
+a replacement of the SysVinit killall5(8) command.
+
+.SH USAGE
+It is not allowed to use
+.B \-h
+(signal SIGHUP) as first option! If you really want this then start:
+.B nkillall
+\-s0 \-hup ...
+
+.TP
+.B \-v
+verbose mode; \-vv means more verbose
+.TP
+.B \-q
+quiet mode; ignores SIGINT signal; Ctrl-C does't work!
+.TP
+.B \-s
+\fIsecs\fR
+.br
+sleep \fIsecs\fR
+.TP
+.B \-M
+\fImesg\fR
+.br
+write \fImesg\fR to stdout. The message can include escape
+symbols:
+\\NNN, \\a, \\b, \\e, \\c, \\f, \\n, \\r, \\t, \\v, \\\\
+like in C language. Last message can be written again with
+\-M% or \-W%. Example:
+
+.B nkillall
+\-M'\\n\\aPlease logout! The system is going down...' \\
+ \-W% \-s2 -W% \-s4 \-W% \-s12 \\
+ \-M'\\tSending TERM signal' \-term ...
+.TP
+.B \-W
+\fImesg\fR
+.br
+write \fImesg\fR to all logged users. The syntax is the same as \-M.
+.B nkillall
+determines logged users by looking at
+.I /var/run/utmp.
+
+.br
+In my opinion
+.I /var/run/utmp
+is unreliable!
+Other programs like who(1), wall(1), login(1) also use this file.
+If you feel that yours is insecure, better don't use the \-W option.
+
+.TP
+.B \-signum
+send all processes the singal \fIsignum\fR.
+Some important signals can be encodded. For example SIGTERM can be typed with
+\-15 or \-term.
+Only the first letter is important!
+The command
+.B kill \-l
+lists the signal numbers.
+.TP
+.B \-E
+.I /path/to/program [arg[s]]
+.br
+execve(2) the program with some arguments.
+This option must be last! The program will have the same PID as
+.B nkillall.
+
+.SH NOTE
+The options are applied immediately. Their position is important.
+The next two examples work differently:
+
+nkillall \-q -s2 \-M'sending signal SIGTERM' \-15 ...
+.br
+nkillall \-s2 \-M'sending signal SIGTERM' \-15 \-q ...
+
+Use the options \-v[v] \-q after
+.B nkillall
+immediately.
+
+.SH WARNING
+.B nkillall
+also kills the script in which it is included.
+Use it in scripts like:
+
+#!/bin/sh
+.br
+before commands
+.br
+exec nkillall [options] \-E/path/to/prog prog_opt(s)
+
+An example of wrong usage is the following:
+
+#!/bin/sh
+.br
+before commands
+.br
+nkillall ...
+.br
+after commands
+
+.SH EXAMPLES
+.B nkillall
+\-M'Sending all processes SIGTERM ...' \-s2 \-15 \\
+.br
+ \-M'Sending all processes SIGKILL ...' \-s6 \-9 \\
+.br
+ \-s1 \-E /path/to/program arg1 arg2 ...
+
+.B nkillall
+\-v \-s1 \-15 \-cont \-s6 \-kill \-s1 \-E/path/to/prog arg(s) ...
+
+.B nkillall
+\-vv \-pause \-s59 \-continue \-M'Hello world!'
+
+.SH FILES
+.I /var/run/utmp
+
+.SH AUTHOR
+.B nkillall
+is included in ninit package and can be downloaded from
+.br
+.I http://riemann.frmi.uni-sofia.bg/ninit/
+
+.SH "SEE ALSO"
+ninit(8), kill(1), kill(2), execve(2), utmp(5)
+.br
+init(8), killall5(8)
diff --git a/man/nsvc.8 b/man/nsvc.8
new file mode 100644
index 0000000..3ac0900
--- /dev/null
+++ b/man/nsvc.8
@@ -0,0 +1,240 @@
+.TH nsvc 8 "Jan 19, 2010"
+.SH NAME
+nsvc \- control ninit
+.SH SYNOPSIS
+.B nsvc
+[ -Sservice ]
+[
+.B \-[uodgpchaitkorRDCLHVWZE]
+]
+.I service
+[\fI...\fR]
+.br
+.B nsvc -Ppid
+.I service
+.
+.SH DESCRIPTION
+.B nsvc
+is the management interface to ninit.
+.I service
+is the service directory name relative to /etc/ninit.
+You can also include /etc/ninit/ in the service name.
+.PP
+Each service directory contains control files.
+They are described on
+.I http://riemann.fmi.uni-sofia.bg/ninit/
+
+.PP
+It is possible to make nsvc SUID.
+.PP
+.RS
+.B chown root.root /bin/nsvc
+.br
+.B chmod 4711 /bin/nsvc
+.RE
+.PP
+Then nsvc opens the pipes
+.IR /etc/ninit/in ,
+.I /etc/ninit/out
+and switches to real UID.
+If UID is nonzero only limited set of operation are allowed.
+.
+.SH OPTIONS
+If no options are given,
+.B nsvc
+will just print a diagnostic message to stdout, saying if the
+service is up, down or finished, which PID it has if it is up, and for
+how long it has been in this state.
+
+Only the service name
+.B ALL
+means all services.
+
+.TP
+.B \-Sservice
+Skip service. Apply this option immediately after
+.B nsvc.
+Don't insert space between S and service.
+Examples:
+.br
+.B nsvc
+\-Sngetty \-Ssshd \-Slogger \-d ALL
+.br
+.B nsvc
+\-Sngetty \-W3 ALL ||
+.B nsvc
+\-Sngetty \-k ALL
+
+.TP
+.B \-u \-uNumber
+Up.
+If the service is not running, start it.
+If the service stops, restart it. If
+.B Number
+is nonzero and the service is down start it after
+.B Number
+seconds.
+.TP
+.B \-o \-oNumber
+Once.
+If the service is down, start it.
+If the service stops, do not restart it.
+If
+.B Number
+is nonzero and the service is down, start it after
+.B Number
+seconds; if it is up, restart it later.
+.TP
+.B \-d
+Down.
+If the service is running, send it a TERM signal and then a CONT signal.
+After it stops, do not restart it.
+.TP
+.B \-r
+Stop respawn.
+Set respawn flag to OFF. This does not start/stop the service.
+.TP
+.B \-R
+Start respawn.
+Set respawn flag to ON. This does not start/stop the service.
+.TP
+.B \-p
+Pause.
+Send the service a STOP signal.
+.TP
+.B \-c
+Continue.
+Send the service a CONT signal.
+.TP
+.B \-h
+Hangup.
+Send the service a HUP signal.
+.TP
+.B \-a
+Alarm.
+Send the service an ALRM signal.
+.TP
+.B \-i
+Interrupt.
+Send the service an INT signal.
+.TP
+.B \-t
+Terminate.
+Send the service a TERM signal.
+.TP
+.B \-k
+Terminate.
+Send the service a KILL signal.
+.TP
+.B \-g
+Get. Output just the PID.
+.TP
+.B \-Ppid \fIservice\fR
+Set pid of service.
+.TP
+.B \-D \fIservice\fR
+Print dependencies.
+This will print all the names of all the services that were started
+because this services depended on them. Example:
+.br
+.B nsvc -D default
+.TP
+.B \-D
+Print ninit memory usage statistics.
+.TP
+.B \-H
+Print history.
+This will print the names of some recently spawned processes.
+This is useful if you see a process looping (initialization fails and
+ninit is restarting it all the time).
+.TP
+.B \-L
+Print all services loaded in memory.
+.TP
+.B \-V
+Print version
+.TP
+.B \-Wnumber
+Wait at most
+.B number
+seconds service(s) to finish. Example:
+.br
+.B nsvc \-d qmail
+.br
+.B nsvc \-W180 qmail || nsvc -k qmail
+.TP
+.B \-Cnumber \-C+number
+Tell ninit that the service is in CRON mode.
+If
+.B number
+is zero it disables CRON mode. Let now=`date +%s`.
+If
+.B number < now
+then ninit starts the service immediately,
+otherwise ninit will start it latter.
+The flag
+.B \-C+number
+(if the
+.B number
+is positive)
+is equaivalent to the following:
+.br
+.B t=`expr now + number`
+.br
+.B nsvc -C$t ...
+
+To stop foo,
+which is in CRON mode do:
+.br
+.B nsvc -C0 foo
+.br
+.B nsvc -d foo
+.br
+.\" To see the next CRON timestamp of foo type:
+.\" .B nsvc foo
+.TP
+.B \-Z \-Znumber
+Zero (free) a service. This is done with the ninit-reload program.
+Example:
+.br
+.B nsvc -Z30 foo
+.br
+This removes foo (if such a service exists)
+and prepares ninit to accept 30 new services approximately.
+.TP
+.B \-E \-Enumber
+Update ninit environ. This is done by ninit-reload program.
+Example:
+.br
+.B nsvc \-E36 ABC=12 UVW
+.br
+This updates the variable
+.B ABC
+and removes
+.B UVW.
+It prepares ninit to accept 36 new services approximately.
+See the environ with:
+.br
+.B tr '\\\\000' '\\\\012' < /proc/1/environ
+
+.
+.SH "RETURN CODES"
+Generally,
+.B nsvc
+returns zero if everything is OK or 1 on error (could not
+open /etc/ninit/in or /etc/ninit/out or there is no process with the
+given name). In diagnostic mode, it will exit 0 if the service is up, 2
+if it is down or 3 if it is finished.
+
+.
+.SH "ENVIRON"
+.B nsvc
+uses the variables
+.B NINIT_MEMORY
+and
+.B NINIT_HOME.
+
+.SH "SEE ALSO"
+ninit(8), ninit-scan(8)
+.br
+ninit-shutdown(8), nkillall(8), svc(8), msvc(8)
diff --git a/man/pidfile.8 b/man/pidfile.8
new file mode 100644
index 0000000..97efe18
--- /dev/null
+++ b/man/pidfile.8
@@ -0,0 +1,75 @@
+.TH ninit\-pidfile 8 "Dec 28, 2009"
+.SH NAME
+ninit\-pidfile \- work around daemons that always fork
+.SH SYNOPSIS
+.B ninit\-pidfile
+.I servicename
+.I pidfile
+[
+.I -H home
+]
+.I command
+.I [parameters]
+
+.SH DESCRIPTION
+.B ninit\-pidfile
+is used to work around daemons that insist on forking into the background,
+but that do write a correct pid file.
+
+.B ninit\-pidfile
+forks the actual service, then waits for the pidfile to
+be written. Once it can read the pid from the pidfile it will tell
+ninit the real pid and quit.
+
+.SH OPTIONS
+.TP
+.I servicename
+the name of the service
+ninit\-pidfile is installed for
+.TP
+.I pidfile
+the filename to read the pid from
+.TP
+.I command
+the real command to start
+.TP
+.I parameters
+additional parameters for the command
+.TP
+.B \-H \fIhome\fR
+.br
+the home of ninit. Default: /etc/ninit
+
+.SH USAGE
+With
+.B ninit
+you can prepare a service and if it forks and writes
+the PID in some file then type in the service directory:
+
+ echo /path/to/deamon.pidfile >
+.I pidfile
+
+For apache this looks like:
+
+ cd /etc/ninit/apache
+.br
+ echo /var/run/apache.pid >
+.I pidfile
+
+Don't use hard or soft links here!
+.br
+Then
+.B ninit
+will start apache using
+.B ninit\-pidfile
+
+.SH AUTHOR
+.B pidfilehack
+was written
+by Felix von Leitner.
+
+This manpage was written by Erich Schubert <[email protected]>
+for the Debian GNU/Linux operating system.
+
+.SH "SEE ALSO"
+ninit(8), nsvc(8), pidfilehack(8)
diff --git a/man/pututmpid.8 b/man/pututmpid.8
new file mode 100644
index 0000000..7bcac33
--- /dev/null
+++ b/man/pututmpid.8
@@ -0,0 +1,73 @@
+.TH pututmpid 8 "Dec 28, 2009"
+.SH NAME
+pututmpid \- write utmp/wtmp records and exec command
+.SH SYNOPSIS
+.B putupmpid
+.I [-w]
+.I ut_id
+.I command
+.I [parameters]
+.br
+.B pututmpid
+.I reboot
+.br
+.B pututmpid
+.I halt
+
+
+.SH DESCRIPTION
+.B pututmpid
+is used to write records in utmp/wtmp files for running
+getty/agetty/fgetty.
+pututmpid writes the record and then execve()s a command.
+The commands
+.B login
+and
+.B getty
+use the records written from pututmpid.
+
+After reboot it is possible to set
+.B pututmpid reboot
+in init scripts.
+This will update /var/run/utmp with BOOT_TIME record. This
+must be done after /var/run/utmp is writable. The usage of
+.B pututmpid halt
+is similar. It sets wtmp record (in /var/log/wtmp file).
+
+.SH OPTIONS
+usually pututmpid is symlinked as \fIrun\fR command of a service.
+.TP
+.B \-w
+debug mode -- write more info in /var/log/wtmp
+.TP
+.B ut_id
+string at most 4 bytes long. examples: tty1,tty2, vc1,vc2,vc3...
+Use different names for each virtual console!
+.TP
+.B command
+the real command to start
+.TP
+.B parameters
+additional parameters for the command
+
+.SH EXAMPLE
+A typical use of this command will look like this:
+.TP
+/etc/ninit/getty/2
+.TP
+/etc/ninit/getty/2/params
+tty2
+.br
+/sbin/fgetty
+.br
+tty2
+.TP
+/etc/ninit/getty/2/run
+-> /sbin/pututmpid
+
+.SH "SEE ALSO"
+ninit(8), ninit\-runlevel(8)
+.br
+getty(8), {n|f|a|min}getty(8),
+.br
+login(1), utmp(5), runlevel(8)
diff --git a/man/reboot.8 b/man/reboot.8
new file mode 100644
index 0000000..c1ef01b
--- /dev/null
+++ b/man/reboot.8
@@ -0,0 +1,59 @@
+.TH ninit\-reboot 8 "Dec 28, 2009"
+.SH NAME
+ninit-reboot \- reboot your system immediately
+.SH SYNOPSIS
+.B ninit\-reboot
+.I RESTART
+.br
+.B niniy\-reboot
+.I HALT
+.br
+.B ninit\-reboot
+.I POWER_OFF
+.br
+.B ninit\-reboot
+.I ENABLE_CAD
+.br
+.B ninit\-reboot
+.I DISABLE_CAD
+
+.SH DESCRIPTION
+.B ninit\-reboot
+is used to reboot your system.
+
+It will not shut down services, unmount filesystems or notify
+your users, but expects that this has already been done when it
+is called.
+
+.SH USAGE
+
+To prevent accidential use of this application the parameters have to
+be written in uppercase letters.
+.TP 10
+.I RESTART
+restart (reboot) the system
+.TP
+.I HALT
+halt the kernel
+.TP
+.I POWER_OFF
+power off the system if possible (supported by hardware)
+.TP
+.I ENABLE_CAD
+halt the system. Enable CTRL\-ALT\-DEL key to restart the system
+after halt.
+.TP
+.I DISABLE_CAD
+halt the system. Disable CTRL\-ALT\-DEL key to restart the system
+after halt.
+
+
+
+.SH AUTHOR
+ninit\-reboot was contributed by Tommi Virtanen.
+
+This manpage was partially written by Erich Schubert <[email protected]>
+for the Debian GNU/Linux operating system.
+
+.SH "SEE ALSO"
+ninit(8), nsvc(8), ninit\-shutdown(8), nkillall(8)
diff --git a/man/reload.8 b/man/reload.8
new file mode 100644
index 0000000..c8ac521
--- /dev/null
+++ b/man/reload.8
@@ -0,0 +1,120 @@
+.TH ninit-reload 8 "Dec 28, 2009"
+.SH NAME
+ninit\-relaod \- replace running ninit with a new version
+.SH SYNOPSIS
+.B ninit\-reload
+[options] [/path/to/ninit] [ninit_options]
+
+.SH DESCRIPTION
+.B ninit\-reload
+is used to replace a running ninit with a new version.
+
+It tries to retrieve the state information about running services from
+ninit, then have ninit replace itself with the new version and
+restore the stored state information.
+
+.SH OPTIONS
+Unless the \-u option is given, ninit\-reload assumes you are running
+in test mode.
+.TP
+\-v
+verbose operation
+.TP
+\-u
+update mode
+.TP
+\-m
+dump ninit memory buffer to stdout
+.TP
+\-d
+dump services data to stdout
+.TP
+\-R
+.I service
+.br
+remove
+.I service
+from active list
+.TP
+\-r
+.I number
+.br
+remove
+service with
+.I number
+from active list. Example:
+.RS
+ ninit\-reload -v /sbin/ninit
+.br
+ ninit\-reload -v -r 3 -R qmail -u /sbin/ninit
+.RE
+.TP
+\-e
+.I string
+.br
+update ninit environ. Example:
+.br
+.B ninit\-reload -v -e ABC=12 -e UVW -u /sbin/ninit
+.br
+This updates the variable
+.B ABC
+and removes
+.B UVW.
+See the environ after that with:
+.B tr '\\\\000' '\\\\012' < /proc/1/environ
+.TP
+\-E
+.I file
+.br
+update ninit environ using
+.I file.
+The syntax for
+.I file
+is the same as in the file
+.I environ
+for services.
+.TP
+\-a
+.I number
+.br
+reload ninit and calculate memory buffer to
+.I number
+additional services. Check the result with:
+.B ps axww.
+.TP
+\-t
+.I time_diff
+.br
+add
+.I time_diff
+seconds to each service. Useful if you change the time with hwclock.
+.I time_diff
+can also be a negative number.
+.TP
+\-f
+.I data_file
+.br
+don't retrieve services from ninit. Use this file instead.
+For example you can start:
+.PP
+.RS
+ ninit-reload -d > /tmp/ninit.data
+.br
+ ninit-reload -v -u -f /tmp/ninit.data /sbin/ninit
+.RE
+.PP
+
+.SH "ENVIRON"
+.B ninit\-reload
+uses the variables
+.B NINIT_MEMORY
+and
+.B NINIT_HOME.
+
+
+.SH AUTHOR
+This manpage was partially written by Erich Schubert <[email protected]>
+for the Debian GNU/Linux operating system.
+
+.SH "SEE ALSO"
+ninit(8), nsvc(8)
diff --git a/man/runlevel.8 b/man/runlevel.8
new file mode 100644
index 0000000..bdb05a6
--- /dev/null
+++ b/man/runlevel.8
@@ -0,0 +1,108 @@
+.TH ninit-runlevel 8 "Jan 16, 2010"
+.SH NAME
+ninit-runlevel
+\- set runlevels for compatibility with SysVinit
+.SH SYNOPSIS
+.B ninit-runlevel
+.B LEVEL
+[OPTION]... [-] [NAME=VALUE]... [PROGRAM [ARG]...]
+.br
+.B ninit-runlevel
+.
+.SH DESCRIPTION
+.B ninit-runlevel
+is a helper program to start service (script, program) with the same
+environ as SysVinit. It also modifies the files
+.I /var/run/utmp
+and
+.I /var/log/wtmp
+writing runlevel records in them.
+It is used mainly in ninit-inittab(8) output script.
+
+It sets the following variables:
+INIT_VERSION=2.86,
+CONSOLE=/dev/console,
+RUNLEVEL,
+PREVLEVEL,
+PATH=/bin:/usr/bin:/sbin:/usr/sbin.
+The variable NINIT_RUNLEVEL always has the same value
+as RUNLEVEL.
+The program does not modify or reset the variable INIT_HALT.
+
+.SH OPTIONS
+Actually
+.B ninit\-runlevel
+has the same syntax between
+.B LEVEL
+and
+.B PROGRAM
+as the command env(1).
+
+If
+.B ninit-runlevel
+is started as a service you can use
+.I environ
+file
+to set some environment variables.
+However in this case
+.B ninit-runlevel
+overwrites the variables:
+INIT_VERSION,
+CONSOLE,
+RUNLEVEL,
+PREVLEVEL,
+PATH
+and NINIT_RUNLEVEL.
+You can reset some of them, for example PATH.
+.TP
+\-i
+start with an empty environment
+.br
+a mere - implies -i
+.TP
+\-u
+.I variable
+.br
+remove the
+.I variable
+from the environment
+
+.SH "EXAMPLES"
+In the next examples we modify PATH, INIT_HALT and CONSOLE
+
+.B ninit-runlevel S
+PATH=/sbin:/bin /etc/rc.d/rc.single
+.br
+.B ninit-runlevel 0
+INIT_HALT=POWERDOWN -u CONSOLE /etc/rc.d/rc.halt
+
+See the environ with (don't start it as root \-
+the program will then modify
+.I /var/run/utmp
+)
+
+.B ninit-runlevel 5
+/usr/bin/env
+
+.SH WARNING
+Don't remove the file
+. I /var/run/utmp
+if you want correct PREVLEVEL and RUNLEVEL variables.
+.B ninit-runlevel
+stores the info about levels there.
+.B ninit
+does not use the
+.I /var/run/utmp
+file, nor PREVLEVEL, RUNLEVEL, NINIT_RUNLEVEL.
+
+.SH "FILES"
+.I /var/run/utmp
+.br
+.I /var/log/wtmp
+
+.SH "SEE ALSO"
+utmp(5), env(1), runlevel(8)
+.br
+ninit(8), ninit\-sysvinit(8), ninit\-shutdown(8), ninit\-inittab(8)
+.br
+init(1), shutdown(8), reboot(8), halt(8)
diff --git a/man/scan.8 b/man/scan.8
new file mode 100644
index 0000000..674a1f9
--- /dev/null
+++ b/man/scan.8
@@ -0,0 +1,99 @@
+.TH ninit\-scan 8 "Jan 19, 2010"
+.SH NAME
+ninit\-scan \- scan directory and start/stop services
+.SH SYNOPSIS
+.B ninit\-scan
+[
+.B \-[uod]
+]
+.I services_directory
+.I ninit_home
+.
+.SH DESCRIPTION
+.B ninit\-scan
+scans a directory to start/stop a collection of services.
+
+Service definitions are installed, configured and activated as subdi-
+rectories of the
+.I services_directory.
+As ninit\-scan sequentially scans the
+.I services_directory,
+it looks for subdirectory names not beginning with `.'. If
+ninit\-scan then finds the `sticky' bit set on the subdirectory,
+it considers the service definition ``active'' and
+attempts to start/stop the corrsponding service.
+
+
+
+.SH OPTIONS
+If no option is given
+.B ninit\-scan
+try to start the service in respawn ON or OFF mode
+depending on the existence of the file
+.B respawn
+in the service subdirectory.
+
+.TP
+.B \-u
+Up.
+If the service is not running, start it.
+If the service stops, restart it.
+.TP
+.B \-o
+Once.
+If the service is down, start it.
+If the service stops, do not restart it.
+.TP
+.B \-d
+Down.
+If the service is running, send it a TERM signal and then a CONT signal.
+After it stops, do not restart it.
+
+.SH EXAMPLES
+
+.B ninit\-scan \-u net /etc/ninit
+.br
+start all services /etc/ninit/net/* which have `sticky' bit set
+in respawn mode.
+
+.B ninit\-scan etc /etc/ninit
+.br
+start all services /etc/ninit/etc/* which have `sticky' bit
+set.
+
+.B ninit\-scan \-d '' /etc/ninit
+.br
+stop all services /etc/ninit/* which have `sticky' bit set.
+
+.SH USAGE
+
+This is similar to the file
+.I depends.
+Put in
+.I rsetup
+the following:
+
+.B #!/bin/sh
+.br
+.B exec $2/bin/ninit\-scan $1 $2
+
+
+The program is shell script. See the code to understand how it works.
+.B ninit\-scan
+is designed to permit easy service activation/deactivation using
+the chmod(1) utility.
+You can set/unset `sticky' bit with
+
+
+chmod +t myservice
+.br
+chmod \-t myservice
+
+.SH AUTHOR
+Nikola Vladov.
+Thanks to Wayne Marshall for the `sticky' bit idea.
+.br
+.I http://b0llix.net/perp/
+
+.SH SEE ALSO
+ninit(8), nsvc(8), perpd(8), chmod(1)
diff --git a/man/service.8 b/man/service.8
new file mode 100644
index 0000000..75c3c33
--- /dev/null
+++ b/man/service.8
@@ -0,0 +1,46 @@
+.TH ninit\-service 8 "Dec 28, 2009"
+.SH NAME
+ninit\-service \- print info about a service
+.SH SYNOPSIS
+.B ninit\-service
+[-OPTIONS] service(s)
+.br
+.B ninit\-service
+-E service file
+.
+.SH DESCRIPTION
+.B ninit\-service
+is a shell script which prints info about service(s).
+It only looks in the service directory.
+Use
+.B nsvc
+to obtain info about services loaded in ninit memory.
+
+.SH OPTIONS
+.TP
+\-A ascii output
+.TP
+\-C show end of lines; for cat
+.TP
+\-L print long lines; for ls
+.TP
+\-E
+.I service file
+.br
+edit service/file;
+default editor: /bin/vi; change it with:
+.br
+echo /usr/bin/emacs -nw > /etc/ninit/.editor
+.TP
+\-H/other/home
+default home: /etc/ninit
+
+.SH EXAMPLE
+.B ninit\-service
+ngetty
+.br
+.B ninit\-service
+`find /etc/ninit/ -type d`
+
+.SH "SEE ALSO"
+ninit(8), nsvc(8), ngetty(8)
diff --git a/man/shutdown.8 b/man/shutdown.8
new file mode 100644
index 0000000..62d6ccf
--- /dev/null
+++ b/man/shutdown.8
@@ -0,0 +1,89 @@
+.TH ninit-shutdown 8 "Dec 28, 2009"
+.SH NAME
+ninit-shutdown \- shutdown the ninit init system.
+.SH SYNOPSIS
+.B ninit-shutdown
+\-[\fIrhoqvstmST\fR] [\-E /path/to/program [arg1 arg2 ...]]
+
+.SH DESCRIPTION
+.B ninit-shutdown
+tries to properly shutdown your system with ninit.
+
+.SH USAGE
+.TP 9
+\-r
+reboot after shutdown
+.TP
+\-h
+halt after shutdown
+.TP
+\-o
+power-off after shutdown (default)
+.TP
+\-q
+quiet mode; ignores SIGINT signal; Ctrl-C does't work!
+.TP
+\-v
+be verbose
+.TP
+\-m
+only shutdown the ninit-part and exit
+.TP
+\-s \fIsecs\fR
+starting delay
+.TP
+\-t \fIsecs\fR
+delay between SIGTERM and SIGKILL
+.TP
+\-E \fIprog\fR
+execute \fIprog\fR after KILLALL; must be the last option!
+.TP
+\-T \fIsecs\fR
+if \fIsecs\fR is nonzero shutdown twice ninit part
+.TP
+\-S \fIabcd\fR
+skip to shutdown the service \fIabcd\fR
+
+.SH NOTES
+If the flags -m and -E are off then the program starts the service
+.I halt
+or
+.I reboot
+depending of the flags -h -o and -r respectively.
+
+If the flags -E and -m are on then
+.B ninit-shutdown
+execve(2)s the
+\fIprog\fR after shutting down the ninit-part.
+
+If the flag -E is on and the flag -m is off then
+.B ninit-shutdown
+execve(2)s
+the \fIprog\fR after sending SIGTERM and SIGKILL to all processes.
+
+.B ninit-shutdown
+clears respawn/cron flags on all services
+including those skipped with -S flag.
+
+.
+.SH "ENVIRON"
+.B ninit-shutdown
+uses the variables
+.B NINIT_MEMORY
+and
+.B NINIT_HOME.
+
+.SH AUTHOR
+.B ninit
+was written by Nikola Vladov and can be downloaded from
+.br
+.I http://riemann.frmi.uni-sofia.bg/ninit/
+
+Parts of
+.B ninit-shutdown
+were contributed by Bernd Wachter.
+.br
+Parts of this manpage was written by Erich Schubert.
+
+.SH "SEE ALSO"
+ninit(8), nsvc(8), execve(2), nkillall(8)
diff --git a/man/sysvinit.8 b/man/sysvinit.8
new file mode 100644
index 0000000..46a0b77
--- /dev/null
+++ b/man/sysvinit.8
@@ -0,0 +1,155 @@
+.TH ninit-sysvinit 8 "Dec 28, 2009"
+.SH NAME
+ninit-sysvinit \- SysVinit emulator listening on /dev/initctl
+.SH SYNOPSIS
+.B ninit-sysvinit
+.br
+.B ninit-sysvinit
+powerS
+
+.SH DESCRIPTION
+.B ninit
+does not use the
+.I /dev/initctl
+fifo. As a result the SysVinit commands
+telinit, reboot, halt, shutdown do not work.
+Some of them need to write to
+.I /dev/initctl
+to work properly.
+
+.B ninit-sysvinit
+is a SysVinit emulator which listens on
+.I /dev/initctl
+and starts some services if there is any activity on the fifo.
+
+.SH USAGE
+Five seconds after starting all services
+.B ninit
+tries to start the sysvinit service.
+If the last service exists then
+.B ninit
+opens and listens on
+.I /dev/initctl.
+.B Never include
+the service sysvinit in
+.I /etc/ninit/default
+nor start it at boot time.
+
+.SH OPTIONS
+.B ninit\-sysvinit
+reads the control file
+.I sysvinit\-timeout
+in the service directory.
+It must contain two numbers n1:n2.
+If there is not
+any activity on
+.I /dev/initctrl
+for more than n1 seconds
+the program exits.
+If the number n1 is zero
+.B ninit\-sysvinit
+wait forever.
+
+If needed,
+.B ninit
+can start
+.B ninit\-sysvinit
+later again.
+
+.SH RUNLEVELS
+SysVinit has runlevels 0123456S. The command
+.br
+.B telinit S
+.br
+writes the request to
+.I /dev/initctl
+and
+.B ninit\-sysvinit
+starts the service
+.I levelS.
+It works similarly with telinit 5, telinit Q or telinit U.
+Then
+.B ninit\-sysvinit
+starts the
+.I level5,
+.I levelQ
+or
+.I levelU
+service if it exists.
+
+.SH "POWER ACTIONS"
+.B ninit\-sysvinit
+also starts the services
+.I powerF,
+.I powerO
+or
+.I powerL.
+The letters come from F(AIL), O(K) or L(OW).
+The program can obtain the power request from
+.I /dev/initctl
+or from a SIGPWR signal and the file
+.I /etc/powerstatus.
+See the manual page init(8) for more info.
+
+.SH SIGNALS
+Don't send signal SIGPWR to
+.B ninit\-sysvinit.
+Manpage init(8) says:
+
+
+Usage of SIGPWR and
+.I /etc/powerstatus
+is discouraged. Someone wanting to
+interact with init should use the
+.I /dev/initctl
+control channel.
+
+If some program still needs to use signal SIGPWR
+it must send it dirrectly to
+.B ninit.
+
+.SH ENVIRON
+SysVinit
+.B shutdown
+sets the variable
+INIT_HALT=POWERDOWN
+or
+INIT_HALT=HALT
+depending on the options
+\-r \-h \-H \-P. You can (re)set this variable
+using:
+
+.B nsvc -E INIT_HALT
+.br
+.B nsvc -E INIT_HALT=POWERDOWN
+
+.SH "AUTHOR"
+If you don't plan to use SysvInit's shutdown, reboot,
+halt or telninit commands, you can do:
+
+.B cd /etc/ninit
+.br
+.B mv sysvinit sysvinit_
+.br
+.B ninit\-reload \-v \-u /sbin/ninit
+
+Why did I write this SysVinit
+.B emulator?
+This is a good question.
+Ninit has the ninit-shutdown, nkillall and ninit-reboot programs. There
+is no need to use SysVinit halt, reboot... to stop the box clean.
+See the code of the emulator. Maybe you will find the answer there.
+
+Nikola Vladov
+
+.SH FILES
+.I /dev/initctl
+.br
+.I /etc/powerstatus
+.br
+.I NINIT_HOME/sysvinit/sysvinit\-timeout
+
+.SH SEE ALSO
+nsvc(8), ninit-shutdown(8), ninit-runlevel(8)
+.br
+init(8), shutdown(8), halt(8), reboot(8), telinit(8)
diff --git a/misc/BIN b/misc/BIN
new file mode 100644
index 0000000..6abcaf0
--- /dev/null
+++ b/misc/BIN
@@ -0,0 +1,48 @@
+v:1:::::
+d:::755:::
+d:::755:/etc::
+d:::755:/etc/ninit::
+
+# d:::755:/etc/ninit/bin::
+# g:::755:/etc/ninit/bin/::
+# x:::::argv0::
+# x:::::env::
+# x:::::conditional-init::
+# x:::::sleeprun::
+# x:::::install-bin::
+# x:::::serdo::
+# x:::::ninit-huge::
+# x:::::ninit-mmap::
+# x:::::ninit-conf:scripts/conf:
+# x:::::ninit-scan:scripts/scan:
+
+g:::600:/etc/ninit/::
+x:::644::.sync:Version:
+x:::644::.nsvc_help:nsvc_help:
+p:::::in:
+p:::::out:
+
+d:::755:/bin::
+x:::755:/bin/:nsvc::
+
+d:::755:/sbin::
+g:::755:/sbin/::
+x:::::ninit-reload:reload:
+x:::::ninit-reboot:reboot:
+x:::::ninit-pidfile:pidfile:
+x:::::ninit-sysvinit:sysvinit:
+x:::::ninit-runlevel:runlevel:
+x:::::bootlog::
+x:::::pututmpid::
+x:::::nkillall::
+x:::::ninit-shutdown:shutdown:
+c:::::ninit-inittab:inittab:
+c:::::ninit-service:scripts/service:
+
+d:::755:/etc/ninit/sys::
+g:::755:/etc/ninit/sys/::
+x:::::run::
+x:::::run-wait:wait:
+x:::::update::
+x:::::remove::
+x:::::procfs::
diff --git a/misc/ETC b/misc/ETC
new file mode 100644
index 0000000..aa55bef
--- /dev/null
+++ b/misc/ETC
@@ -0,0 +1,18 @@
+d:::755:/etc::
+d:::755:/etc/ninit::
+
+g:::600:/etc/ninit/::
+x:::644::.sync:Version:
+x:::644::.nsvc_help:nsvc_help:
+
+v:1:::::
+p:::::in:
+p:::::out:
+
+d:::755:/etc/ninit/sys::
+g:::755:/etc/ninit/sys/::
+x:::::run::
+x:::::run-wait:wait:
+x:::::update::
+x:::::remove::
+x:::::procfs::
diff --git a/misc/HOME b/misc/HOME
new file mode 100644
index 0000000..91b9944
--- /dev/null
+++ b/misc/HOME
@@ -0,0 +1,25 @@
+v:1:::::
+d:::755:::
+
+g:::755:::
+d::::/sys::
+d::::/default::
+d::::/env::
+d::::/sleep::
+d::::/sh::
+d::::/S::
+
+g:::600:/::
+x:::644::.sync:Version:
+x:::644::.nsvc_help:nsvc_help:
+p:::::in:
+p:::::out:
+
+c:::755:/env/:env::
+
+g:::755:/sys/::
+x:::::run::
+c:::::run-wait:wait:
+c:::::update::
+c:::::remove::
+c:::::procfs::
diff --git a/misc/MAN b/misc/MAN
new file mode 100644
index 0000000..f91815d
--- /dev/null
+++ b/misc/MAN
@@ -0,0 +1,19 @@
+v:1:::::
+d:::755:::
+d:::755:/man8::
+
+g:::644:/man8/::
+x:::::ninit.8.gz::
+x:::::nsvc.8.gz::
+x:::::ninit-scan.8.gz:scan.8.gz:
+x:::::bootlog.8.gz::
+x:::::nkillall.8.gz::
+x:::::pututmpid.8.gz::
+x:::::ninit-shutdown.8.gz:shutdown.8.gz:
+x:::::ninit-runlevel.8.gz:runlevel.8.gz:
+x:::::ninit-sysvinit.8.gz:sysvinit.8.gz:
+x:::::ninit-pidfile.8.gz:pidfile.8.gz:
+x:::::ninit-inittab.8.gz:inittab.8.gz:
+x:::::ninit-service.8.gz:service.8.gz:
+x:::::ninit-reload.8.gz:reload.8.gz:
+x:::::ninit-reboot.8.gz:reboot.8.gz:
diff --git a/misc/child_block.c b/misc/child_block.c
new file mode 100644
index 0000000..284fb73
--- /dev/null
+++ b/misc/child_block.c
@@ -0,0 +1,9 @@
+#include <sys/types.h>
+#include <signal.h>
+
+void child_block(int type) /*EXTRACT_INCL*/{ /* SIG_BLOCK, SIG_UNBLOCK */
+ sigset_t mask;
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGCHLD);
+ sigprocmask(type, &mask, 0);
+}
diff --git a/misc/dup2_inout.c b/misc/dup2_inout.c
new file mode 100644
index 0000000..13132d8
--- /dev/null
+++ b/misc/dup2_inout.c
@@ -0,0 +1,13 @@
+#include <unistd.h>
+#include <fcntl.h>
+
+void dup2_inout(char *file, int from, mode_t mode) /*EXTRACT_INCL*/ {
+ int to = open(file, mode);
+ if (to == from || to == -1) return;
+ while (1) {
+ dup2(to,from);
+ fcntl(from,F_SETFD,0);
+ if (from++ != 1) break;
+ }
+ close(to);
+}
diff --git a/misc/fmt_argv.c b/misc/fmt_argv.c
new file mode 100644
index 0000000..a61f8db
--- /dev/null
+++ b/misc/fmt_argv.c
@@ -0,0 +1,10 @@
+#include "../ninitfeatures.h"
+
+int fmt_argv(int fd, char **argv, const char *sep) /*EXTRACT_INCL*/ {
+ int len=0;
+ for (; argv[len]; len++) {
+ errmsg_puts(fd, argv[len]);
+ errmsg_puts(fd, (char *)sep);
+ }
+ return len;
+}
diff --git a/misc/initreq.c b/misc/initreq.c
new file mode 100644
index 0000000..c0160f0
--- /dev/null
+++ b/misc/initreq.c
@@ -0,0 +1,31 @@
+
+#include <stdio.h>
+#include <unistd.h>
+#include "../initreq.h"
+
+int main() {
+ struct init_request req;
+ while (1) {
+ if (read(0, &req, sizeof(req)) != (int)sizeof(req)) return 1;
+
+ printf("magic:\t%x\n" "cmd:\t%d\n" "runlevel:\t%d=%c\n"
+ "sleeptime:\t%d\n"
+ "reserved:\t%s\n"
+ "exec_name:\t%s\n"
+ "host:\t%s\n"
+ "term_type:\t%s\n"
+ "tty_id:\t%s\n"
+ "gen_id:\t%s\n"
+ "\n",
+ req.magic, req.cmd, req.runlevel, req.runlevel,
+ req.sleeptime,
+ req.i.bsd.reserved,
+ req.i.bsd.exec_name,
+ req.i.bsd.host,
+ req.i.bsd.term_type,
+ req.i.bsd.tty_id,
+ req.i.bsd.gen_id
+ );
+ }
+ return 0;
+}
diff --git a/misc/opendevconsole.c b/misc/opendevconsole.c
new file mode 100644
index 0000000..3f92223
--- /dev/null
+++ b/misc/opendevconsole.c
@@ -0,0 +1,12 @@
+#include <fcntl.h>
+#include <unistd.h>
+
+void opendevconsole() /*EXTRACT_INCL*/ {
+ int fd;
+ if ((fd=open("/dev/console",O_RDWR|O_NOCTTY))>=0) {
+ dup2(fd,0);
+ dup2(fd,1);
+ dup2(fd,2);
+ if (fd>2) close(fd);
+ }
+}
diff --git a/misc/set_sigaction.c b/misc/set_sigaction.c
new file mode 100644
index 0000000..2b429a6
--- /dev/null
+++ b/misc/set_sigaction.c
@@ -0,0 +1,10 @@
+#include <signal.h>
+extern void sighandler(int sig);
+
+void set_sigaction(int sig) /*EXTRACT_INCL*/{
+ struct sigaction sa;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_handler=sighandler;
+ sa.sa_flags=SA_RESTART | SA_NOCLDSTOP;
+ sigaction(sig, &sa, 0);
+}
diff --git a/misc/sulogin.c b/misc/sulogin.c
new file mode 100644
index 0000000..6f0aa26
--- /dev/null
+++ b/misc/sulogin.c
@@ -0,0 +1,9 @@
+#include <unistd.h>
+#include <stdlib.h>
+extern char **environ;
+
+void sulogin() /*EXTRACT_INCL*/ {
+ char *argv[2] = {"sulogin",0};
+ execve("/sbin/sulogin",argv,environ);
+ _exit(1);
+}
diff --git a/misc/system_child_block.c b/misc/system_child_block.c
new file mode 100644
index 0000000..7032f71
--- /dev/null
+++ b/misc/system_child_block.c
@@ -0,0 +1,9 @@
+#define SIGACTION_FUNCTIONS
+#include "../process_defs.h"
+extern int rt_sigprocmask();
+
+void system_child_block(int type) /*EXTRACT_INCL*/ {
+ /* SIG_BLOCK, SIG_UNBLOCK */
+ STATIC_SIGCHLD_MASK;
+ rt_sigprocmask(type,&sigchld_mask,0,sizeof(sigchld_mask));
+}
diff --git a/misc/system_set_sigaction.c b/misc/system_set_sigaction.c
new file mode 100644
index 0000000..04515c4
--- /dev/null
+++ b/misc/system_set_sigaction.c
@@ -0,0 +1,42 @@
+#define SIGACTION_FUNCTIONS
+#include "../process_defs.h"
+
+typedef void (*sighandler_t)(int);
+typedef struct { unsigned long sig[SIGSET_MASK_LEN_N]; } sigset_t;
+
+/* copy/paste from dietlibc! Felix, you are great! */
+struct sigaction {
+#if defined(__alpha__) || defined(__ia64__) || defined(__hppa__)
+ sighandler_t sa_handler;
+ unsigned long sa_flags;
+ sigset_t sa_mask;
+#elif defined(__mips__)
+ unsigned long sa_flags;
+ sighandler_t sa_handler;
+ sigset_t sa_mask;
+ void (*sa_restorer)(void);
+ int32_t sa_resv[1];
+#else /* arm, i386, ppc, s390, sparc, saprc64, x86_64 */
+ sighandler_t sa_handler;
+ unsigned long sa_flags;
+ void (*sa_restorer)(void);
+ sigset_t sa_mask;
+#endif
+};
+
+extern void sighandler(int sig);
+extern void __restore();
+extern void __restore_rt();
+extern int rt_sigaction();
+#include "../byte_defs.h"
+
+void system_set_sigaction(int sig) /*EXTRACT_INCL*/ {
+ struct sigaction sa;
+ byte_zero(&sa, sizeof(sa));
+ sa.sa_handler=sighandler;
+ sa.sa_flags=SA_FLAGS_number;
+#ifndef INIT_SKIP_SIGRETURN
+ sa.sa_restorer=&(SA_RESTORER_function);
+#endif
+ rt_sigaction(sig, &sa, 0, sizeof(sigset_t));
+}
diff --git a/misc/try_helper.h b/misc/try_helper.h
new file mode 100644
index 0000000..bdc7319
--- /dev/null
+++ b/misc/try_helper.h
@@ -0,0 +1,29 @@
+unsigned int s_len(const char * s) {
+ unsigned int len=0;
+ while (s[len]) ++len;
+ return len;
+}
+
+char *fmt_ul(unsigned long u) {
+ static char strnum[48];
+ char *hex = "0123456789abcdef";
+ char *s = strnum+44;
+ *s = 0;
+ do { *--s = hex[(u % 16)]; u /= 16; } while(u); /* handles u==0 */
+ *--s = 'x';
+ *--s = '0';
+ return s;
+}
+
+char *fmt_o(unsigned char c) {
+ static char x[8];
+ x[4] = 0;
+ x[3] = '0' + (c & 7); c >>= 3;
+ x[2] = '0' + (c & 7); c >>= 3;
+ x[1] = '0' + (c & 7);
+ x[0] = '\\';
+ return x;
+}
+
+void w(char *s) { write(1,s,s_len(s)); }
+void wn(unsigned long u) { w(fmt_ul(u)); }
diff --git a/misc/try_int.h2 b/misc/try_int.h2
new file mode 100644
index 0000000..c010eaf
--- /dev/null
+++ b/misc/try_int.h2
@@ -0,0 +1,5 @@
+#ifndef INT_DEFS_H
+#define INT_DEFS_H
+typedef unsigned Z uint32t;
+typedef Z int32t;
+#endif
diff --git a/misc/trypagesize.c b/misc/trypagesize.c
new file mode 100644
index 0000000..33c7cbb
--- /dev/null
+++ b/misc/trypagesize.c
@@ -0,0 +1,5 @@
+#include <unistd.h>
+int main() {
+ if (getpagesize() == 4096) return 0;
+ return 1;
+}
diff --git a/misc/tryprocess.c b/misc/tryprocess.c
new file mode 100644
index 0000000..821e670
--- /dev/null
+++ b/misc/tryprocess.c
@@ -0,0 +1,132 @@
+#include "../ninitfeatures.h"
+#include <unistd.h>
+#include <signal.h>
+#include "try_helper.h"
+
+#define ioctl libc_ioctl
+#include <termios.h>
+#undef ioctl
+#include <sys/ioctl.h>
+
+#define w_sn_(a,b) w(a); wn(b); w("\n")
+#define w_sp_(a,b,c) w(a); wn((void *)(&b) - (void *)(&c)); w("\n")
+#define w_sns(a,b,c) w(a); wn(b); w(c)
+#define w_ss_(a,b) w(a); w(b); w("\n")
+#define w_o(X) w(fmt_o((unsigned char)X))
+#define w_to(t,X) w(t); w_o(X)
+
+#include "../struct_root.h"
+INIT_ROOT_DEFINE(ch, char*);
+INIT_ROOT_DEFINE(ui, unsigned int);
+
+#define UL sizeof(unsigned long)
+#define MASK_LEN (_NSIG/(8 * UL))
+#define WORD(X) ((X-1)/(8*UL))
+#define MASK(X) (((unsigned long)1) << (X-1) % (8*UL))
+
+int main() {
+ int i,k;
+ struct sigaction sa;
+ unsigned long uu[256];
+ char *ss[4] = { "kbreq", "ctrlaltdel", "powerS", "levelU" };
+
+ w("#define NINIT_SERVICES_CODED\t\"");
+ for (k=4, i=0; i<4; i++) {
+ w_o(k);
+ k += 1 + s_len(ss[i]);
+ }
+ for (i=0; i<4; i++) {
+ w(ss[i]);
+ if (i<3) w("\\000");
+ }
+ w("\"\n");
+
+ if (SIGWINCH < 256 && SIGINT < 256 && SIGPWR < 256 && SIGHUP < 256) {
+ w("#define NINIT_SIGNAL_HANDLER_CODED\t\"");
+ w_o(SIGWINCH); w_o(SIGINT); w_o(SIGPWR); w_o(SIGHUP);
+ w("\"\n");
+ }
+
+ if (SIGTERM < 256 && SIGALRM < 256 && SIGSTOP < 256 &&
+ SIGCONT < 256 && SIGHUP < 256 && SIGINT < 256 && SIGKILL < 256) {
+ w("#define NSVC_SIGNAL_CODED\t\"");
+ w_to("t",SIGTERM); w_to("a",SIGALRM); w_to("p",SIGSTOP);
+ w_to("c",SIGCONT); w_to("h",SIGHUP); w_to("i",SIGINT);
+ w_to("k",SIGKILL);
+ w("\"\n");
+ }
+
+ w_sn_("#if 0\n\tdev_t\t", sizeof(dev_t));
+ w_sn_("\tgid_t\t", sizeof(gid_t));
+ w_sn_("\tuid_t\t", sizeof(uid_t));
+ w_sn_("\tmode_t\t", sizeof(mode_t));
+
+ w_sn_("#endif\n#define X_TIOCGPGRP\t", TIOCGPGRP);
+ w("\n#ifdef SIGACTION_FUNCTIONS\n\n");
+
+#ifndef SA_RESTORER
+#define SA_RESTORER 0x04000000
+#endif
+
+ w_sn_("#define SA_RESTART\t", SA_RESTART);
+ w_sn_("#define SA_NOCLDSTOP\t", SA_NOCLDSTOP);
+
+ uu[0] = SA_RESTART | SA_NOCLDSTOP | SA_RESTORER;
+#ifdef INIT_SKIP_SIGRETURN
+ w("#define INIT_SKIP_SIGRETURN\n");
+ uu[0] = SA_RESTART | SA_NOCLDSTOP;
+#endif
+ w_sn_("#define SA_FLAGS_number\t", uu[0]);
+
+#if defined(__i386__)
+ w("#define SA_RESTORER_function\t__restore");
+ if (uu[0] & SA_SIGINFO) w("_rt");
+#endif
+
+#if defined(__x86_64__)
+ w("#define SA_RESTORER_function\t__restore_rt");
+#endif
+ w("\n");
+
+ w("\n#if 0\n");
+ /* w_sn_("#define STAT_SIZE\t", sizeof(struct stat)); */
+ w_sn_("#define SIGATION_SIZE\t", sizeof(sa));
+ w_sn_("#define SIGSET_T_SIZE\t", sizeof(sa.sa_mask));
+ w_sn_("#define SIGSET_WORD_N\t", WORD(SIGCHLD));
+ w_sn_("#define SIGSET_MASK_N\t", MASK(SIGCHLD));
+
+ w_sp_("\tsa_handler offset\t", sa.sa_handler, sa);
+ w_sp_("\tsa_flags offset\t", sa.sa_flags, sa);
+ w_sp_("\tsa_restorer offest\t",sa.sa_restorer, sa);
+ w_sp_("\tsa_mask offset\t", sa.sa_mask, sa);
+ w("#endif\n\n");
+
+ for (i=0; i<256; i++) uu[i] = 0;
+ uu[WORD(SIGCHLD)] = MASK(SIGCHLD);
+ w_sn_("#define SIGSET_MASK_LEN_N\t", MASK_LEN);
+ w_sns("#define STATIC_SIGCHLD_MASK unsigned long sigchld_mask[",
+ MASK_LEN, "] = { ");
+
+ for (i=0;; ) {
+ wn(uu[i]);
+ if (++i == MASK_LEN) break;
+ w(", ");
+ }
+
+ w(" }\n"
+ "\n#else\n"
+ "#include \"struct_root.h\"\n");
+
+ if (sizeof(struct ch) > sizeof(struct ui)) {
+ w("#define INIT_NAME_IS_UINT\n");
+ w_sn_("#define PROCESS_SIZE ", sizeof(struct ui));
+ w("INIT_ROOT_DEFINE(process, uint32t) *root;\n");
+ } else {
+ w("#undef INIT_NAME_IS_UINT\n");
+ w_sn_("#define PROCESS_SIZE ", sizeof(struct ch));
+ w("INIT_ROOT_DEFINE(process, char*) *root;\n");
+ }
+
+ w("#endif\n");
+ return 0;
+}
diff --git a/misc/tryulong32.c b/misc/tryulong32.c
new file mode 100644
index 0000000..e0adc6d
--- /dev/null
+++ b/misc/tryulong32.c
@@ -0,0 +1,6 @@
+int main() {
+ unsigned long u=1,k=0;
+ for (; k<32; k++) u += u;
+ if (!u) return 0;
+ return 1;
+}
diff --git a/mmap_alloca.h b/mmap_alloca.h
new file mode 100644
index 0000000..20b9d89
--- /dev/null
+++ b/mmap_alloca.h
@@ -0,0 +1,51 @@
+#ifndef INIT_MMAP
+
+#define depends_alloca(N) alloca(N)
+#define read_alloca(N) alloca(N)
+#define INIT_FREE_MEMBLOCKS(X) X
+#define free_execve(...) execve(__VA_ARGS__)
+
+
+#else
+#include <sys/mman.h>
+#include <sys/shm.h> /* for PAGE_SIZE */
+#ifndef PAGE_SIZE
+#define PAGE_SIZE 4096
+#endif
+
+#define depends_alloca(N) mmap_alloca(N)
+#define read_alloca(N) \
+ ((read_buf) ? read_buf : (read_buf=mmap_alloca(N)))
+
+#define INIT_FREE_MEMBLOCKS(X) read_buf=0; mmap_free(); X
+#define free_execve(...) mmap_free(); execve(__VA_ARGS__)
+
+#define mem_x_size ((sizeof(struct memalloc) + 16) & ~15)
+static struct memalloc *mroot;
+static void *read_buf;
+
+static void *mmap_alloca(uint32t n) {
+ n = (n + 16) & ~15;
+ if (mroot == 0 || n + mem_x_size >= mroot->r) {
+ struct memalloc *m;
+ uint32t len = (n + (PAGE_SIZE + mem_x_size)) & ~(PAGE_SIZE-1);
+ m = mmap(0,len,PROT_READ|PROT_WRITE,MAP_ANONYMOUS|MAP_PRIVATE,-1,0);
+ if (m == (struct memalloc *)-1) return 0;
+ m->x = mroot; /* previous block */
+ m->a = len;
+ m->r = len;
+ mroot = m;
+ }
+ mroot->r -= n;
+ return (void *)mroot + mroot->r;
+}
+#undef mem_x_size
+
+static void mmap_free() {
+ while (mroot) {
+ void *x = mroot->x;
+ munmap(mroot,mroot->a);
+ mroot = x;
+ }
+}
+#endif
diff --git a/ninit.c b/ninit.c
new file mode 100644
index 0000000..39158b2
--- /dev/null
+++ b/ninit.c
@@ -0,0 +1,345 @@
+#define INIT_PROGRAM
+#include "ninit.h"
+
+/* load a service into the process data structure and return index or -1
+ * if failed */
+static int loadservice(char *s) {
+ struct process tmp;
+ int fd;
+ if (*s==0 || *s=='#') return -1;
+ fd=findservice(s);
+ if (fd>=0) return fd;
+ if (chdir(initroot) || chdir(s)) return -1;
+#ifdef INIT_CREATE_INOUT_PIPE
+ if (SYS_mknod("log/in",S_IFIFO|0600,0)==0) symlink("log/in","out");
+#endif
+ byte_zero(&tmp, sizeof(tmp));
+ tmp.startedat = time(0);
+ tmp.pr_respawn = (access("respawn", R_OK)==0);
+ return addprocess(&tmp, s);
+}
+
+static void childhandler();
+static int do_poll(struct pollfd *pfd, int timeout) {
+ int k, n = timeout & 3;
+ for (k=0; k<n; k++) {
+ pfd[k].events = POLLIN;
+ pfd[k].revents = 0;
+ }
+ CHILD_UNBLOCK;
+ k=poll(pfd,n,timeout);
+ CHILD_BLOCK;
+ if (k<0) {
+ if (errno==EINTR) childhandler();
+#ifdef INIT_POLL_FAILED
+ else { write(1,"poll failed!\n",13); sulogin(); }
+#endif
+ }
+ return k;
+}
+
+static int startbyind(int service, int father) {
+ struct process *pr = root + service;
+ char *name = process_name(pr);
+ pid_t pid;
+ struct timespec req = {0,500000000};
+
+ if (pr->pid || pr->pr_circular) return service;
+ pr->pr_circular=1;
+ pr->father=father;
+ pr->cron=0;
+#if INIT_HISTORY > 0
+ for (pid = INIT_HISTORY; pid>1; --pid) history[pid] = history[pid-1];
+ history[1]=service+1;
+#endif
+ if (chdir(initroot)) return -1;
+ while ((pid=fork()) < 0) nanosleep(&req,0);
+ if (pid==0) {
+ CHILD_UNBLOCK;
+ if (chdir(initsysdir)==0) {
+ char *aa[5];
+ INIT_ARGS5(aa, pr->pr_end ? "end" : "./run",name,initroot,initsysdir,0);
+ if (time(0) == pr->startedat) nanosleep(&req,0);
+ free_execve("./run",aa,environ);
+ }
+ _exit(1);
+ }
+ pr->pid=pid;
+ pr->startedat=time(0);
+ if (chdir(name)==0) {
+ int fd;
+ if (pr->pr_end) pr->pr_end = 0;
+ else pr->pr_end = (access("end",X_OK)==0);
+
+ if ((fd=open("sync", O_RDONLY)) >= 0) {
+ struct pollfd pfd[2];
+ unsigned int u;
+ byte_zero(pfd, sizeof(pfd));
+ read(fd, pfd, sizeof(pfd)-1);
+ close(fd);
+
+ if ((u=atoulong((void *)pfd)) == 0) u = -1;
+ pfd->fd = -1;
+ do {
+ do_poll(pfd, 1001); /* must be 4k+1 */
+ if (pr->pid < 2) break;
+ } while (u--);
+ }
+ }
+ return service;
+}
+
+static int startbyname(char *name,int father) {
+ char *s;
+ int fd,len,service=loadservice(name);
+ if (service<0) return -1;
+ name = root_name(service);
+#ifdef INIT_BLACK
+ if (father>=0)
+ for (len=1; (s=Argv[len]); len++)
+ if (*s=='#' && !str_diff(s+1,name))
+ return service;
+#endif
+#ifdef INIT_LOG_SERVICE
+ len = str_len(name);
+ s = alloca(len+5); if (s==0) return -1;
+ byte_copy(s,len,name);
+ byte_copy(s+len,5,"/log");
+ startbyname(s,service);
+#endif
+
+ if (chdir(initroot) || chdir(name)) return -1;
+ if ((fd = open("depends", O_RDONLY)) >= 0) {
+ char *p, exitasapl=0;
+ if (GLOBAL_READ(fd,s, len,32000)) { close(fd); return 0; }
+ close(fd);
+ s[len]=0;
+ for (p=s; ;p=++s) {
+ while (*s && *s!='\n') s++;
+ if (*s==0) ++exitasapl;
+ *s=0;
+ startbyname(p,service);
+ if (exitasapl) break;
+ }
+ }
+ return startbyind(service,father);
+}
+
+/* return -1 on error */
+static int restartservice(char *s) {
+ int n=loadservice(s);
+ if (n>=0 && root[n].pid<2) {
+ if (start_time) root[n].cron = start_time;
+ else {
+ root[n].pid=0;
+ circsweep();
+ n=startbyname(s,-1);
+ }
+ }
+ start_time = 0;
+ return n;
+}
+
+static void handlekilled(int ind) {
+ struct process *pr = root + ind;
+ next_cron = 0;
+ pr->pr_finish = 1;
+ pr->pid = 0;
+ if (pr->pr_end || pr->pr_respawn) {
+ circsweep();
+ startbyind(ind,pr->father);
+ } else {
+ pr->startedat=time(0);
+ pr->pid += 1;
+ }
+}
+
+static void childhandler() {
+ int ind;
+ pid_t pid;
+ if (do_update) return;
+ while ((pid=waitpid(-1,0,WNOHANG))) {
+ if (pid < 0) {
+#ifdef INIT_EXIT_WARNING
+ static char saidso;
+ if (!saidso) { write(2,"all services exited.\n",21); ++saidso; }
+#endif
+ return;
+ }
+ for (ind=0; ind<=maxprocess; ind++)
+ if (root[ind].pid==pid) {
+ handlekilled(ind);
+ break;
+ }
+ }
+}
+
+static void do_initctl(struct pollfd *pfd) {
+ int fd;
+ if (restartservice("sysvinit") >= 0) {
+ if ((fd=open(INIT_FIFO,O_RDWR)) >= 0)
+ initctl=fd;
+ } else ++initctl;
+ pfd[1].fd = initctl;
+ fcntl(pfd[1].fd,F_SETFD,FD_CLOEXEC);
+}
+
+static void read_infd() {
+ char *b1, *other, *buf;
+ struct process *pr;
+ int idx, len, offset;
+ uint32t u32;
+
+ buf = read_alloca(384);
+ if (buf==0 || (len=read(infd,buf,380)) <2) return;
+
+ b1 = buf + 1;
+ buf[len] = 0; buf[len+1] = 0;
+ offset = str_len(buf) + 1;
+ other = buf + offset;
+ u32 = atoulong(other);
+ next_cron = 0;
+
+ switch (buf[0]) {
+ case 'D':
+ if (*b1=='M') t_write(mem.x,mem.a);
+ else {
+ if (*b1=='1') do_update = 1;
+ t_write(mem.x+mem.r, mem.a-mem.r); /* names + history */
+ t_write(root, mem.l); /* precess */
+ }
+ break;
+ case 'U': /* U\0argv[0] or Uservice\0 */
+ if (*b1==0) {
+ char *aa[3]; INIT_ARGS3(aa, other, "", 0);
+ execve(other,aa,environ);
+ } else
+ if (len >= offset + PROCESS_SIZE &&
+ addprocess(other,b1) >= 0) goto ok;
+ goto error;
+ case 's':
+ start_time = u32;
+ if (restartservice(b1) < 0) goto error;
+ok: t_write("1",1); break;
+error: t_write("0",1); break;
+
+ default:
+ if ((idx=findservice(b1)) <0) goto error;
+ pr = root + idx;
+
+ switch(buf[0]) {
+ case 'r': pr->pr_respawn = (other[0] != 0); goto write_pr;
+ case 'P':
+ if ((pid_t)u32<2 || kill(u32,0)) goto error;
+ pr->pid = u32; goto write_pr;
+ case 'c': pr->cron = u32;
+ case 'p':
+write_pr:
+ t_write(pr, sizeof(struct process));
+ }
+ }
+}
+
+int main(int argc, char *argv[]) {
+ struct pollfd pfd[12];
+ time_t now;
+ int i;
+#ifdef INIT_BLACK
+ Argv=argv;
+#endif
+ mem.r = INIT_ALLOC_BUFFER;
+ for (i=1; i<argc; i++)
+ if (argv[i][0]=='-') {
+ char *p = argv[i]+2;
+ switch (p[-1]) {
+ case 'M': mem.r = atoulong(p); break;
+#ifdef INIT_HOME
+ case 'H': initroot = p; break;
+ case 'S': initsysdir = p; break;
+#endif
+ default: p[-2] = '#'; /* -sshd --> #sshd */
+ }
+ }
+
+ mem.r &= 0xfff8; /* max 64K */
+ mem.a = mem.r + (INIT_HISTORY+1) * sizeof(unsigned short);
+ mem.x = alloca(mem.a); /* if it fails bummer */
+ history = mem.x + mem.r;
+ byte_zero(history, (INIT_HISTORY+1) * sizeof(unsigned short));
+ ((unsigned char*)history)[1] = INIT_HISTORY;
+
+ if (getpid()==1) {
+ set_reboot(0);
+ i = open("/dev/console",O_RDWR|O_NOCTTY);
+ if (i>0) { dup2(i,0); close(i); }
+ ioctl(0, KDSIGACCEPT, SIGWINCH);
+ }
+
+ set_sa(SIGCHLD);
+ set_sa(SIGINT); /* ctrl-alt-del */
+ set_sa(SIGWINCH); /* keyboard request */
+#ifdef INIT_SYSVINIT_SIGNALS
+ set_sa(SIGPWR); /* ifdef INIT_POWERSTATUS start powerZ */
+ set_sa(SIGHUP); /* ifdef INIT_SIGHUP start levelU */
+ set_sa(SIGUSR1); /* ifdef INIT_SIGUSR1 reopen /dev/initctl */
+#endif
+ CHILD_BLOCK;
+
+ if (chdir(initroot) ||
+ (infd=open("in",O_RDWR)) < 0 ||
+ (outfd=open("out",O_RDWR|O_NONBLOCK)) < 0) {
+#ifdef INIT_PIPES_ERROR
+ write(1,"pipes error\n",12); sulogin();
+#endif
+ } else {
+ int fd=open(".sync", O_RDONLY);
+ while ((i=read(fd,pfd,sizeof(pfd))) > 0) write(1,pfd,i);
+ close(fd);
+ }
+
+ fcntl(infd,F_SETFD,FD_CLOEXEC);
+ fcntl(outfd,F_SETFD,FD_CLOEXEC);
+
+ if (argv[argc-1][0] == 0) {
+ ++do_update;
+ t_write("8",1);
+ } else {
+ for (i=1; i<argc; i++) restartservice(argv[i]);
+ if (maxprocess < 0) restartservice("default");
+ }
+
+ pfd[0].fd = infd;
+ pfd[1].fd = -1;
+
+ for (;;) {
+ for (i=0; i<4; i++)
+ if (got_sig[i]) {
+ char *xx = NINIT_SERVICES_CODED;
+ got_sig[i]=0; restartservice(xx + xx[i]);
+ }
+ childhandler();
+
+ if ((time(&now)) > next_cron) {
+ time_t t; next_cron = now + 1800;
+ for (i=0; i<=maxprocess; i++) {
+ struct process *pr = root + i;
+ if (pr->pid > 1 && kill(pr->pid, 0)) handlekilled(i);
+
+ if (pr->pid < 2 && (t=pr->cron) > 0) {
+ if (t < now) restartservice(root_name(i));
+ if (t < next_cron) next_cron = t;
+ }
+ }
+ }
+
+ if ((i=do_poll(pfd, 5002)) == 0) { /* timeout; must be 4k+2 */
+ INIT_FREE_MEMBLOCKS(do_update=0);
+ if (initctl < -1) do_initctl(pfd);
+ continue;
+ }
+ if (i>0) {
+ if (pfd[0].revents) read_infd();
+ if (pfd[1].revents) restartservice("sysvinit");
+ }
+ }
+}
diff --git a/ninit.h b/ninit.h
new file mode 100644
index 0000000..4a4bd07
--- /dev/null
+++ b/ninit.h
@@ -0,0 +1,82 @@
+#include "ninitfeatures.h"
+#include "process_defs.h"
+
+#ifndef INIT_NAME_IS_UINT
+#define root_name(J) root[J].name
+#define process_name(P) P->name
+#define alloc_name_set(N) N
+#define process_name_set name
+#define root_names_len (mem.x + mem.a - (void*)root[0].name)
+
+#else
+#define root_name(J) (mem.x + root[J].name)
+#define process_name(P) (mem.x + P->name)
+#define alloc_name_set(N) ((void*)N - mem.x)
+#define process_name_set mem.r
+#define root_names_len (mem.a - root[0].name)
+#endif
+
+#ifdef INIT_PROGRAM
+#include <time.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/poll.h>
+#include <sys/wait.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/reboot.h>
+#include <linux/kd.h>
+#include "initreq.h"
+#endif
+
+#if ((INIT_HISTORY<0) || (INIT_HISTORY>255))
+#error ***** INIT_HISTORY must be between 0 and 255 *****
+#endif
+
+static int infd, outfd;
+static int maxprocess=-1;
+static struct memalloc mem;
+
+#ifdef INIT_PROGRAM
+
+#ifdef INIT_HOME
+static char *initroot = INITROOT;
+static char *initsysdir = INITSYSDIR;
+#else
+#define initroot INITROOT
+#define initsysdir INITSYSDIR
+#endif
+
+#ifdef INIT_BLACK
+static char** Argv;
+#endif
+
+static char do_update;
+static time_t next_cron;
+static uint32t start_time;
+static int initctl = -5;
+static unsigned short *history;
+
+#include "findservice.h"
+#include "addprocess.h"
+#include "t_write.h"
+#include "mmap_alloca.h"
+#include "sighandler.h"
+#include "uid.h"
+
+#ifndef INIT_CHILD_BLOCK
+#define CHILD_BLOCK
+#define CHILD_UNBLOCK
+#else
+#if INIT_SYSTEM
+#define CHILD_BLOCK system_child_block(SIG_BLOCK)
+#define CHILD_UNBLOCK system_child_block(SIG_UNBLOCK)
+#else
+#define CHILD_BLOCK child_block(SIG_BLOCK)
+#define CHILD_UNBLOCK child_block(SIG_UNBLOCK)
+#endif
+#endif
+
+#endif
diff --git a/ninit.spec b/ninit.spec
new file mode 100644
index 0000000..6c1fd3e
--- /dev/null
+++ b/ninit.spec
@@ -0,0 +1,60 @@
+Summary: small init with build-in SVC and cron
+Name: ninit
+Version: 0.14
+Release: 1
+Group: System Environment/Daemons
+Packager: Nikola Vladov <[email protected]>
+Source: http://riemann.fmi.uni-sofia.bg/ninit/ninit-%{version}.tar.bz2
+URL: http://riemann.fmi.uni-sofia.bg/ninit/
+License: GPL
+Prefix: /
+BuildRoot: %{_tmppath}/%{name}-%{version}-root
+
+%description
+
+Ninit is a small daemon which can be PID 1. It has build-in SVC and cron.
+Read more about it on http://riemann.fmi.uni-sofia.bg/ninit/
+
+%prep
+
+%setup -q
+
+%build
+MYARCH=`uname -m | sed -e's/i[4-9]86/i386/' -e's/armv[3-6]t\?e\?[lb]/arm/'`
+test "i386" != ${MYARCH} -a "x86_64" != ${MYARCH} && MYARCH=withdiet
+make ${MYARCH} prefix=${RPM_BUILD_ROOT}
+test -d bin-${MYARCH} && cp bin-${MYARCH}/* .
+
+%install
+mkdir -p ${RPM_BUILD_ROOT}/usr/share/man
+make install DESTDIR=${RPM_BUILD_ROOT}
+rm -f ${RPM_BUILD_ROOT}/etc/ninit/in ${RPM_BUILD_ROOT}/etc/ninit/out
+
+%clean
+rm -rf ${RPM_BUILD_ROOT}
+
+%post
+test -p /etc/ninit/in || mkfifo -m 600 /etc/ninit/in
+test -p /etc/ninit/out || mkfifo -m 600 /etc/ninit/out
+
+aa=`readlink /proc/1/exe`
+if test "$aa" = "/sbin/ninit" ; then
+ /sbin/ninit-reload -v -u /sbin/ninit
+else
+ echo See the home page of ninit how to prepare the host to boot
+ echo with /sbin/ninit instead of default /sbin/init.
+ echo http://riemann.fmi.uni-sofia.bg/ninit/
+fi
+%postun
+
+%files
+%defattr(-,root,root)
+/sbin
+/usr
+/etc
+/bin
+%doc README CHANGES
+
+%changelog
+* Thu May 14 2008 Nikola Vladov <[email protected]>
+- Create rpm
diff --git a/ninitfeatures.h b/ninitfeatures.h
new file mode 100644
index 0000000..be2ab39
--- /dev/null
+++ b/ninitfeatures.h
@@ -0,0 +1,199 @@
+/* bellow is the variable INIT_PRIVATE_SETUP
+ Fill private changes there! */
+
+#ifndef NINITFEATURES_H
+#define NINITFEATURES_H
+
+#define INITROOT "/etc/ninit"
+#define INITSYSDIR "sys"
+
+#define INIT_POWERSTATUS "/etc/powerstatus"
+#define SYSVINIT_VERSION "2.86"
+
+ /* comment next line if you don't have system flock */
+#define HAVE__NR_flock
+
+ /* checks the environ vars: NINIT_HOME and INIT_HOME.
+ default is only the first */
+// #define INIT_TWO_ENV_HOME
+
+ /* if your kernel has sigreturn function uncomment next line */
+// #define INIT_SKIP_SIGRETURN
+
+
+ /* ------ setup for serdo ----- */
+
+ /* simple echo; don't support esacpes: \a \t ... */
+#define SERDO_WANT_echo
+
+ /* see manpage killall5(8) */
+#define SERDO_WANT_killall5
+
+ /* VAR1=val1 VAR2=val2 ... command ...; see sh(1) */
+#define SERDO_WANT_environ_in_command
+
+ /* max total new variables for export */
+#define SERDO_MAX_NEW_ENVIRON 128
+
+ /* Next is a big security hole! It's similar to dot in PATH
+ Idea: some user type: echo /bin/rm -rf / > /tmp/script
+ and the sysadmin execs serdo in /tmp */
+// #define SERDO_EXEC_script
+
+
+ /* ------ setup for run ----- */
+
+ /* default maximal timout for waiting a service to finish */
+#define RUN_MAXWAIT 600
+
+ /* if defined check gid file in the service directory */
+// #define RUN_WANT_GID_FILE
+
+
+ /* ------ setup for nsvc ----- */
+
+ /* /etc/ninit/ops/// --> ops; only for nsvc */
+#define CLEAN_SERVICE
+
+
+ /* ----- setup for ninit ----- */
+#ifdef INIT_PROGRAM
+
+ /* must be between 0 and 255. length of history; */
+#define INIT_HISTORY 31
+
+ /* if a service has dir log it is startded first.
+ you can skip this and include all log in depends. */
+#define INIT_LOG_SERVICE
+
+ /* ninit automatic creates in/out named pipes for log-services */
+#define INIT_CREATE_INOUT_PIPE
+
+ /* next tree lines are debug warnings.
+ you can comment them if ninit works stable. */
+#define INIT_PIPES_ERROR
+
+ /* warning if poll filed; never happen */
+#define INIT_POLL_FAILED
+
+ /* say that all services are exited. more simple: nsvc -L */
+#define INIT_EXIT_WARNING
+
+ /* ninit will use different service dir with option -H.
+ example: /sbin/ninit -H/etc/ninit.test
+ The {in,out} pipes must be links to /etc/ninit/{in,out}.
+ This is because they are coded in nsvc and ninit-reload. */
+#define INIT_HOME
+
+ /* sigaction on SIGUSR1, SIGPWR, SIGHUP. Comment next to uncatch
+ above signals. Catching them makes ninit 68 bytes longer. */
+#define INIT_SYSVINIT_SIGNALS
+
+ /* skip services with names '-blabla' or '#blabla' on startup */
+// #define INIT_BLACK
+
+ /* unblock/block SIGCHLD near to main poll loop */
+// #define INIT_CHILD_BLOCK
+
+ /* if you use ninit with -Mn and n>3500 uncomment next line.
+ see total/used memory with:
+ ninit-reload -m | wc -c
+ nsvc -D */
+// #define INIT_TIMEOUT_WRITE
+
+ /* uses mmap for depends instead of alloca. If your depeneds
+ files are too large try this ;-) */
+// #define INIT_MMAP
+#endif
+
+
+/* one service (i386) uses 21 + strlen(service) bytes RAM */
+#ifndef INIT_ALLOC_BUFFER
+#ifdef INIT_PROGRAM
+#ifndef INIT_MMAP
+#define INIT_ALLOC_BUFFER 1536
+#else
+#define INIT_ALLOC_BUFFER 2800
+#endif
+#endif
+
+#ifdef SVC_PROGRAM
+#define INIT_ALLOC_BUFFER 3840
+#endif
+#endif
+
+#ifndef INIT_ALLOC_BUFFER
+#define INIT_ALLOC_BUFFER 8192
+#endif
+
+// #define INIT_PRIVATE_SETUP
+
+#ifdef INIT_PRIVATE_SETUP
+// #undef INIT_HOME
+#undef INIT_BLACK
+#undef INIT_SYSVINIT_SIGNALS
+
+#undef INIT_PIPES_ERROR
+#undef INIT_POLL_FAILED
+#undef INIT_EXIT_WARNING
+
+#undef INIT_LOG_SERVICE
+#undef INIT_CREATE_INOUT_PIPE
+// #define SYSVINIT_DUMP_INITREQ
+#endif
+
+/* -------- stop changes here ;-) ---------- */
+#ifdef INIT_PROGRAM
+#ifndef INIT_HISTORY
+#define INIT_HISTORY 0
+#endif
+
+#ifndef INIT_LOG_SERVICE
+#undef INIT_CREATE_INOUT_PIPE
+#endif
+#endif
+
+#ifndef INIT_TIMEOUT_WRITE
+#if (INIT_ALLOC_BUFFER + 0 > 3600)
+#define INIT_TIMEOUT_WRITE
+#endif
+#endif
+
+#include <sys/types.h>
+#include "all_defs.h"
+
+#define GLOBAL_fstat_READ(fd,st,s, len,max_len) \
+ system_fstat(fd,&st) || (unsigned int)(len=st.st_size) > max_len || \
+ (s=alloca(len+1))==0 || read(fd,s,len) != len
+
+#define GLOBAL_READ(fd,s, len,max_len) \
+ (len=lseek(fd,0,SEEK_END)) < 0 || \
+ len > max_len || lseek(fd,0,SEEK_SET) || \
+ (s=alloca(len+1)) == 0 || read(fd,s,len) != len
+
+extern char **environ;
+extern const char *errmsg_argv0;
+
+#define INIT_ARGS1(Z,a) Z[0]=a
+#define INIT_ARGS2(Z,a,b) do { Z[0]=a; Z[1]=b; } while(0)
+#define INIT_ARGS3(Z,a,b,c) do { Z[0]=a; Z[1]=b; Z[2]=c; } while(0)
+#define INIT_ARGS4(Z,a,b,c,d) do { Z[0]=a; Z[1]=b; Z[2]=c; Z[3]=d; } while(0)
+#define INIT_ARGS5(Z,a,b,c,d,e) do { Z[0]=a; Z[1]=b; Z[2]=c; Z[3]=d; Z[4]=e; } while(0)
+#define INIT_ARGS6(Z,a,b,c,d,e,f) do { Z[0]=a; Z[1]=b; Z[2]=c; Z[3]=d; Z[4]=e; Z[5]=f; } while(0)
+#define INIT_ARGS7(Z,a,b,c,d,e,f,g) do { Z[0]=a; Z[1]=b; Z[2]=c; Z[3]=d; Z[4]=e; Z[5]=f; Z[6]=g; } while(0)
+#define INIT_ARGS8(Z,a,b,c,d,e,f,g,h) do { Z[0]=a; Z[1]=b; Z[2]=c; Z[3]=d; Z[4]=e; Z[5]=f; Z[6]=g; Z[7]=h; } while(0)
+
+#define msg(...) err(1,__VA_ARGS__,(char*)0)
+#define carp(...) err(2,__VA_ARGS__,(char*)0)
+#define die(n,...) do { err(2,__VA_ARGS__,(char*)0); _exit(n); } while(0)
+#define errmsg_iam(X) errmsg_argv0 = X
+
+#ifndef INIT_TWO_ENV_HOME
+#define INIT_ENV_GET_HOME(H,A,B) H=env_get(A)
+#else
+#define INIT_ENV_GET_HOME(H,A,B) H=env_get(A); if (!H) H=env_get(B)
+#endif
+#undef INIT_TWO_ENV_HOME
+
+#define BUFFER_TMP_LEN 1500
+#endif /* end of file */
diff --git a/nkillall.c b/nkillall.c
new file mode 100644
index 0000000..0abcaef
--- /dev/null
+++ b/nkillall.c
@@ -0,0 +1,179 @@
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+#include <signal.h>
+#include <alloca.h>
+#include <utmp.h>
+#include <errno.h>
+#include "ninitfeatures.h"
+#include "process_defs.h"
+#include "utmp_defs.h"
+#include "uid.h"
+#include "check_opt.h"
+
+static int cfg_verbose;
+
+void sighandler(int sig) { (void)sig; }
+
+static void deep_sleep(char *x) {
+ time_t t = x_atoi(x);
+ time_t deedline = time(0) + t;
+ if (t<=0) return;
+ if (cfg_verbose>1) msg("Sleeping: ", x);
+ while (1) {
+ nano_sleep(1,0);
+ if (time(0) >= deedline) break;
+ }
+}
+
+static void print_escape(char *x, int wall) {
+ char ch, *y, *start, *from = "abefnrtv\\c", *to= "\a\b\033\f\n\r\t\v\\";
+ unsigned long len;
+ start = alloca(str_len(x) + 3);
+ for (y=start; (ch=*x); x++) {
+ if (ch == '\\') {
+ unsigned long u;
+ len = scan_8ulong(x+1, &u);
+ if (len) { /* 012...9 */
+ ch = u;
+ x += len;
+ } else {
+ len = str_chr(from, x[1]);
+ switch (from[len]) {
+ case 0: break;
+ case 'c': goto do_it;
+ default: /* \a\b... */ ++x; ch = to[len];
+ }
+ }
+ }
+ *y++ = ch;
+ }
+ *y++ = '\n';
+
+ do_it:
+ len = y-start;
+
+ if (wall==0) write(1, start, len);
+ else {
+ struct utmp u;
+ int fd, r;
+ char line[8 + sizeof(u.ut_line)];
+
+ fd = open(_PATH_UTMP, O_RDONLY | O_NOCTTY);
+ if (fd < 0) return;
+
+ while (utmp_io(fd, &u, F_RDLCK)) {
+ if (u.ut_type != USER_PROCESS) continue;
+ if (u.ut_user[0] == 0 || u.ut_pid < 2 ||
+ u.ut_line[0] == 0 || u.ut_line[0] == '/') continue;
+
+ y = line;
+ y += str_copyn(y, "/dev/", 8);
+ y += str_copyn(y, u.ut_line, sizeof(u.ut_line));
+ y[0] = 0;
+ if (line[str_chr(line, '.')]) continue;
+
+ if (kill(u.ut_pid, 0) && errno == ESRCH) continue;
+ r = open(line, O_WRONLY | O_NOCTTY | O_NDELAY);
+ if (r < 0) continue;
+
+ write(r, start, len);
+ close(r);
+ }
+ close(fd);
+ }
+}
+
+#define Kill_Help \
+"Usage:\n",me, \
+" -[vq] [-s secs] [-M|W mesg] [-signum] ... [-E program [arg[s]]\n" \
+"\t-s secs:\tsleep secs\n" \
+"\t-M mesg:\twrite mesg to stdout\n" \
+"\t-W mesg:\twrite mesg to logged users\n" \
+"\t-sugnum:\tsignal number\n" \
+"\t-v[v]:\t\tbe verbose\n" \
+"\t-q:\t\tquiet mode; ignores SIGINT signal\n\n" \
+"Some signals can be codded. Only the first letter is important!\n" \
+"\t-p:\tSIGSTOP\n" \
+"\t-c:\tSIGCONT\n" \
+"\t-h:\tSIGHUP\n" \
+"\t-a:\tSIGALRM\n" \
+"\t-i:\tSIGINT\n" \
+"\t-t:\tSIGTERM\n" \
+"\t-k:\tSIGKILL\n\n" \
+"Option -E must be last. Examples:\n ", \
+me," -s2 -M'Sending all processes SIGTERM ...' -term -cont \\\n" \
+"\t-s6 -M'Sending all processes SIGKILL ...' -9 \\\n" \
+"\t-s1 -E /path/to/program arg1 arg2 ...\n\n ", \
+me," -q -vv -s1 -15 -cont -s6 -kill -s1" \
+" -E/path/to/prog arg(s) ..."
+
+int main(int argc, char **argv) {
+ char *x, *me = argv[0];
+ char *last_msg = "\\c";
+ errmsg_iam(me);
+ if (argc<2) {
+ help:
+ errmsg_iam(0);
+ carp(Kill_Help);
+ _exit(1);
+ }
+ argv++;
+ x = argv[0];
+ if (x[0]=='-' && x[1]=='h') goto help; /* -h... --help */
+
+ opendevconsole();
+
+ /* ignore next signals */
+ set_sa(SIGQUIT); set_sa(SIGCHLD); set_sa(SIGHUP );
+ set_sa(SIGTSTP); set_sa(SIGTTIN); set_sa(SIGTTOU);
+
+ while (argv[0] && argv[0][0]=='-') {
+ int sig=0, cfg_wall=0;
+ char *y = argv[0] + 1;
+ if ((unsigned int)(*y - '0') < 10) {
+ sig = x_atoi(y);
+ goto again;
+ }
+
+ switch(*y) {
+ case 'v': cfg_verbose++; if (y[1]=='v') cfg_verbose++; break;
+ case 'q': set_sa(SIGINT); break;
+ case 's': chk_opt(argv,x); deep_sleep(x); break;
+ case 'W': ++cfg_wall;
+ case 'M': chk_opt(argv,x);
+ if (x[0] == '%' && x[1] == 0) x = last_msg;
+ else last_msg = x;
+ print_escape(x, cfg_wall);
+ break;
+ case 'E':
+ chk_opt(argv,x); *argv = x;
+ execve(argv[0], argv, environ);
+ carp("Unable to exec: ", argv[0]);
+ _exit(1);
+ default:
+#ifdef NSVC_SIGNAL_CODED
+ {
+ unsigned char *S, *Sig = (unsigned char *)NSVC_SIGNAL_CODED;
+ for (S=Sig; *S; S += 2)
+ if ((unsigned char)*y == *S) { sig = S[1]; goto again; }
+ }
+#else
+#define kk(C,S) case C: sig=S; break
+ kk('t',SIGTERM); kk('a',SIGALRM); kk('p',SIGSTOP); kk('c',SIGCONT);
+ kk('h',SIGHUP); kk('i',SIGINT); kk('k',SIGKILL);
+#endif
+ goto help;
+ }
+
+ again:
+ if (sig > 0) {
+ sync();
+ if (cfg_verbose) msg("Sending signal: ", y);
+ if (kill(-1, sig)) carp("Unable to send signal: ", y);
+ }
+ argv++;
+ }
+ sync();
+ return 0;
+}
diff --git a/nsvc.c b/nsvc.c
new file mode 100644
index 0000000..7a89255
--- /dev/null
+++ b/nsvc.c
@@ -0,0 +1,415 @@
+#define SVC_PROGRAM
+#include <fcntl.h>
+#include <time.h>
+#include <sys/file.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include "ninit.h"
+#include "nsvc_buffer.h"
+
+extern int ioctl();
+
+static char **Names_skip, **NamesX, *buf, *initroot, *nsvc_other, respmode;
+static int NamesI, do_update, tty = -111;
+
+static void e(int n) { buffer_flush(buffer_1); _exit(n); }
+static void die_xx(char *s) { errmsg_iam("nsvc"); err(2,s,0); _exit(1); }
+
+#include "get_services.h"
+#include "findservice.h"
+#include "open_inout.h"
+#include "tryservice_nsvc.h"
+
+static char *get_name() {
+ char *x, *s, **skp;
+ again:
+ if (NamesI >= 0) {
+ x = (NamesI <= maxprocess) ? root_name(NamesI) : 0;
+ ++NamesI;
+ } else {
+ x = *NamesX;
+ ++NamesX;
+ }
+ if (x)
+ for (skp=Names_skip; (s=*skp); skp++)
+ if (!str_diff(s,x)) goto again;
+ return x;
+}
+
+static void close_inout() {
+ close(infd); infd = -1;
+ close(outfd); outfd = -1;
+}
+
+static int __findservice(char *service) {
+ int idx;
+ get_services();
+ idx=findservice(service);
+#ifdef CLEAN_SERVICE
+ if (idx<0) idx=findservice(cleanservice(service));
+#endif
+ return idx;
+}
+
+void print_pid(char *service, pid_t pid) {
+ errmsg_iam("nsvc");
+ carp(service,(pid < 0) ? ": no such service" : ": service not running");
+}
+
+/* return PID, -1 if service is not loaded */
+static pid_t __readpid(char *service, int verbose) {
+ pid_t idx=__findservice(service);
+ if (idx >= 0) idx = root[idx].pid;
+ if (verbose && idx<2)
+ print_pid(service, idx);
+ return idx;
+}
+
+static char *fu_x(uint32t u, int len) { /* len must be max 10 !!! */
+ char *x = fu(u);
+ int y = str_len(x);
+ while (y < len) { *--x = ' '; y++; }
+ return x;
+}
+
+static void fs_x(const char *s, int len) {
+ int k = str_len(s);
+ outs(s);
+ if (k >= len) outc('\t');
+ else while (k++ < len) outc(' ');
+}
+
+static void fmt_time(unsigned long up) {
+ unsigned long u;
+ u = up % 60; up /= 60;
+ fmt_color(fu_x(u, 4),'6'," ");
+ if (up) { u = up % 60; up /= 60; fmt_color(fu_x(u, 2),'4'," "); }
+ if (up) { u = up % 24; up /= 24; fmt_color(fu_x(u, 2),'5'," "); }
+ if (up) fmt_color(fu_x(up,4),'2',"");
+}
+
+static int fmt_service(int i, int flagtty) {
+ struct process *pr = root + i;
+ pid_t pid = pr->pid;
+ time_t cr, uptime;
+ static time_t now;
+
+ if (tty==-111) {
+#if defined(INIT_SYSTEM)
+ pid_t dummy;
+ tty = (ioctl(1, X_TIOCGPGRP, &dummy) == 0);
+#else
+ tty=isatty(1);
+#endif
+ }
+
+ if (!now) now=time(0);
+ uptime = now - pr->startedat;
+ if (uptime<0) uptime=0;
+
+ if (flagtty || tty) {
+ char *name=root_name(i);
+ if (flagtty<=4 && tty) {
+ char flags[8], *x, ch;
+ int len;
+
+ if (errmsg_argv0) { outs(errmsg_argv0); outs(": "); }
+ fs_x(name, 16);
+
+ switch (pid) {
+ case 0: x=" down"; ch='7'; break;
+ case 1: x="finish"; ch='2'; break;
+ default:
+ if (pr->pr_respawn) ch='1'; else ch='4';
+ x=fu_x(pid,6);
+ }
+ fmt_color(x,ch," ");
+
+ len=0;
+ if (pr->pr_respawn) flags[len++] = 'R';
+ if (pr->cron) flags[len++] = 'C';
+ if (pr->pr_end) flags[len++] = 'E';
+ flags[len] = 0 ;
+ fs_x(flags, 3);
+
+ fmt_time(uptime);
+
+ if ((cr = pr->cron)) {
+ fmt_color("\tnext cron",'1',":");
+ if (cr > now) fmt_time(cr-now);
+ else outs(" waititing");
+ outc('\t');
+ fmt_color(fu(cr), '8', "");
+ }
+ outc('\n');
+ }
+ else
+ msg(name);
+ } else {
+ errmsg_iam(0);
+ msg(fu(pid)," ",fu(uptime));
+ }
+
+ if (pid==0) return 2; else if (pid==1) return 3; else return 0;
+}
+
+static void dumphistory() {
+ int j;
+ if (!maxhistory) {
+ carp("ninit compiled without history support");
+ return;
+ }
+ errmsg_iam(0);
+ for (j=0; j<maxhistory; j++)
+ if (--history[j] <= maxprocess) {
+ fmt_service(history[j], 4+root[history[j]].pr_circular);
+ root[history[j]].pr_circular = 1;
+ }
+}
+
+static void dumpdependencies(char* service, unsigned int skip) {
+ int i,idx;
+ idx=__findservice(service);
+ if (idx<0) { print_pid(service, -1); return; }
+
+ errmsg_iam(0);
+ for (i=0; i<=maxprocess; ++i) {
+ if (root[i].father==idx && i!=idx) {
+ char *name = root_name(i);
+ int k=skip;
+ while (k-->0) outs(" ");
+ msg(name);
+ if (skip<16) dumpdependencies(name,skip+1);
+ else msg(buf," ...",name);
+ }
+ }
+}
+
+static void wait_childs(int len) {
+ unsigned wtime;
+ pid_t i, pids[len];
+ char *x;
+
+ for (len=0; (x=get_name());)
+ if ((pids[len]=__readpid(x, 0)) > 1) ++len;
+
+ close_inout();
+ wtime = 4*atoulong(nsvc_other);
+
+ while (1) {
+ for (i=len; i>0;)
+ if (kill(pids[--i],0) && errno != EPERM)
+ pids[i] = pids[--len];
+ if (len==0) _exit(0);
+ if (wtime--==0) _exit(1);
+ nano_sleep(0,250000000);
+ }
+}
+
+static int try_srv(char *s, char mode) {
+ int r;
+ char *save = nsvc_other;
+ if (mode == 'r')
+ nsvc_other = (respmode) ? "c" : 0; /* must be odd char !!! */
+ r = tryservice(s, mode);
+ nsvc_other = save;
+ return r;
+}
+
+static void get_timestamp() {
+ char *x = nsvc_other;
+ unsigned int now;
+ if (x[-1] == 'C') {
+ if (x[0] == '+') ++x;
+ else return;
+ }
+ if ((now = atoulong(x)))
+ nsvc_other = fu(time(0) + now);
+}
+
+int main(int argc,char *argv[]) {
+ int i,idx,ret=0;
+ pid_t pid;
+ char *x, **skp;
+
+ __b_1.x = alloca(BUFFER_1_LEN);
+ buf = alloca(BUFFER_TMP_LEN);
+ mem.a = INIT_ALLOC_BUFFER;
+
+ x = env_get("NINIT_MEMORY");
+ if (x) mem.a = atoulong(x);
+ mem.r=mem.a;
+ if (mem.a==0 || mem.a > 64000 || (mem.x=alloca(mem.a))==0) _exit(1);
+
+ errmsg_iam("nsvc");
+ infd = -1; outfd = -1;
+ INIT_ENV_GET_HOME(initroot,"NINIT_HOME","INIT_HOME");
+
+ Names_skip = alloca(argc * sizeof(char *));
+ for (skp=Names_skip; (x=argv[1]); argv++) {
+ if (x[0] != '-' || x[1] != 'S') break;
+ *skp++ = x+2;
+ }
+ *skp = 0;
+
+ {
+ int uid=getuid(), euid=geteuid();
+ if (initroot && (uid != euid)) { ops: die_xx("\ago away!"); }
+ if (initroot==0) initroot=INITROOT;
+
+ if (argv[1] == 0) { setuid(uid); goto REMOVE; }
+ open_inout(initroot);
+
+ if (uid != euid) {
+ if (setuid(uid)) goto ops;
+ get_services();
+ close_inout();
+ }
+ }
+
+ nsvc_other = argv[1] + 2;
+
+ if (argv[2]==0) {
+ char *a1 = argv[1];
+ get_services();
+ close_inout();
+
+ if (a1[0] == '-') {
+ switch (a1[1]) {
+ case 'V': goto REMOVE;
+ case 'L':
+ errmsg_iam(0);
+ while (get_name()) fmt_service(NamesI-1, 1);
+ e(0);
+ case 'H':
+ dumphistory();
+ e(0);
+ case 'D':
+ msg("memory usage: ",fu(mem.l + root_names_len)," = ",
+ fu(mem.l),"+",fu(root_names_len)," = services+names [",
+ fu(maxprocess+1),"]");
+ e(0);
+ }
+ }
+
+ i=__findservice(a1);
+ if (i==-1) { print_pid(a1, -1); e(1); }
+ ret=fmt_service(i,0);
+
+ if (i >= 0 && tty && root[i].pid > 1) {
+ INIT_ARGS2(argv, "./sys/procfs", fu(root[i].pid));
+ goto PROCFS;
+ }
+ } else {
+ char *x;
+ unsigned int sig=0;
+
+ x = argv[2];
+
+ if (argv[1][0]=='-') {
+ char flagdown=1, try_flag='r';
+ respmode = 0;
+
+ switch (argv[1][1]) {
+ case 'P':
+ ret=try_srv(x, 'P');
+ if (ret) carp("Could not set PID of service ",x);
+ break;
+
+ case 'D': dumpdependencies(x,0); break;
+ case 'E':
+ case 'Z':
+ REMOVE:
+ argv[0] = "./sys/remove"; /* XXX: make sys as environ! */
+ PROCFS:
+ close_inout();
+ buffer_flush(buffer_1);
+ ret = chdir(initroot);
+ if (!ret) ret=execve(argv[0], argv, environ);
+ break;
+ default:
+
+ if (argv[3] == 0 && !str_diff(argv[2], "ALL")) {
+ get_services();
+ argc = maxprocess + 4;
+ } else {
+ NamesX = argv + 2;
+ NamesI--;
+ }
+
+ switch (argv[1][1]) {
+ case 'W':
+ wait_childs(argc);
+ case 'g':
+ while ((x=get_name())) {
+ pid=__readpid(x,1);
+ if (pid > 1) { errmsg_iam(0); msg(fu(pid)); }
+ else ret = 1;
+ }
+ break;
+ case 'u': respmode=1;
+ case 'o':
+ try_flag='s';
+ goto try_it;
+
+ case 'C': try_flag='c'; /* cron */
+ case 'R': respmode=1;
+
+ try_it:
+ case 'r': flagdown=0;
+ case 'd':
+ get_timestamp();
+ while ((x=get_name())) {
+ idx=try_srv(x, try_flag);
+ if (idx==0) {
+ if (try_flag=='s') idx=try_srv(x,'r');
+ else if (flagdown) {
+ pid=__readpid(x,0);
+ if (pid>1 && 0==kill(pid,SIGTERM)) kill(pid,SIGCONT);
+ }
+ }
+ if (idx) { print_pid(x,-1); ret=1; }
+ }
+ break;
+
+ default:
+#ifdef NSVC_SIGNAL_CODED
+ {
+ unsigned char *S, *Sig = (unsigned char *)NSVC_SIGNAL_CODED;
+ for (S=Sig; *S; S += 2)
+ if (argv[1][1] == *S) { sig = S[1]; break; }
+ }
+#else
+#define kk(C,S) case C: sig=S; break
+ kk('t',SIGTERM); kk('a',SIGALRM); kk('p',SIGSTOP); kk('c',SIGCONT);
+ kk('h',SIGHUP); kk('i',SIGINT); kk('k',SIGKILL);
+#endif
+ } /* inner switch */
+ } /* outer switch */
+ }
+
+ if (sig) {
+ while ((x=get_name())) {
+ pid=__readpid(x,1);
+ if (pid < 2)
+ ret=1;
+ else if (kill(pid,sig)) {
+ char* s;
+ switch (errno) {
+ case EINVAL: s="invalid signal"; break;
+ case EPERM: s="permission denied"; break;
+ case ESRCH: s="no such pid"; break;
+ default: s="unknown error";
+ }
+ carp(x,": could not send signal ",fu(sig)," to PID ",fu(pid),": ",s);
+ ret=1;
+ }
+ }
+ }
+ }
+ e(ret);
+ return 0;
+}
diff --git a/nsvc_buffer.h b/nsvc_buffer.h
new file mode 100644
index 0000000..6a73158
--- /dev/null
+++ b/nsvc_buffer.h
@@ -0,0 +1,19 @@
+#include "djb/buffer.h"
+
+#undef msg
+#define msg(...) err_b(buffer_1,__VA_ARGS__,0)
+
+#define BUFFER_1_LEN 3200
+buffer __b_1 = BUFFER_INIT(write, 1, 0, BUFFER_1_LEN);
+buffer *buffer_1 = &__b_1;
+
+static void outs(const char *X) { buffer_puts(buffer_1, X); }
+static void outc(char X) { buffer_putc(buffer_1, X); }
+
+static void fmt_color(char *src,char color,char *suffix) {
+ char ops[] = ""; ops[5]=color;
+ outs(ops); ops[2]='0'; ops[5]='9';
+ outs(src);
+ outs(ops);
+ outs(suffix);
+}
diff --git a/nsvc_help b/nsvc_help
new file mode 100644
index 0000000..e1eeb0d
--- /dev/null
+++ b/nsvc_help
@@ -0,0 +1,23 @@
+usage: nsvc [-Sservice] -[uodpchaitkgrWRDHLCEZV] service(s)
+ nsvc -Ppid service
+ -u up start service with respawn; also -un
+ -o once start service without respawn; also -on
+ -d down set respawn off, stop service
+ -R|r respawn set on|off
+ -p pause send SIGSTOP
+ -c cont send SIGCONT
+ -h hangup send SIGHUP
+ -a alarm send SIGALRM
+ -i intr send SIGINT
+ -t term send SIGTERM
+ -k kill send SIGKILL
+ -g get output just the PID
+ -Z Zero free service; also -Zn
+ -E update ninit environ; also -En
+ -D list dependencies
+ -Wn wait n secs service to finish
+ -H print last n services
+ -L list services
+ -Ctime set CRON of service; also -C+time
+ -Ppid set PID of service
+ -Ssrv skip service
diff --git a/open_inout.h b/open_inout.h
new file mode 100644
index 0000000..1a8a61b
--- /dev/null
+++ b/open_inout.h
@@ -0,0 +1,43 @@
+#include <sys/file.h>
+#define NANO_wait \
+ if (nsec < 500000000) nsec *= 2;\
+ else carp("could not acquire lock on: ",home,"/in");\
+ nano_sleep(0,nsec)
+
+static void open_inout(char *home) {
+ char *x,*y;
+ uint32t nsec = 3906249;
+ int len=str_len(home);
+
+ x=alloca(len+6); if (x==0) _exit(1);
+ y=x+len;
+ while (len--) x[len] = home[len];
+
+ y[0]='/'; y[1]='i'; y[2]='n'; y[3]=0;
+ infd = open(x, O_WRONLY);
+
+ y[1]='o'; y[2]='u'; y[3]='t'; y[4]=0;
+ outfd = open(x, O_RDONLY);
+
+ if (infd<0 || outfd<0) die(1, "could not open ",home,"/[in|out]");
+
+#ifdef HAVE__NR_flock
+ while (flock(infd, LOCK_EX | LOCK_NB)) { NANO_wait; }
+#else
+
+#ifndef INIT_SYSTEM
+ while (lockf(infd, F_LOCKW, 1)) { NANO_wait; }
+#else
+ {
+ struct flock fl;
+ byte_zero(&fl, sizeof(fl));
+ fl.l_whence=SEEK_CUR;
+ fl.l_len = 1;
+ fl.l_type = F_WRLCK;
+ while (fcntl(infd, F_SETLKW, &fl)) { NANO_wait; }
+ }
+#endif
+#endif
+}
+
+#undef NANO_wait
diff --git a/ops b/ops
new file mode 100644
index 0000000..39158b2
--- /dev/null
+++ b/ops
@@ -0,0 +1,345 @@
+#define INIT_PROGRAM
+#include "ninit.h"
+
+/* load a service into the process data structure and return index or -1
+ * if failed */
+static int loadservice(char *s) {
+ struct process tmp;
+ int fd;
+ if (*s==0 || *s=='#') return -1;
+ fd=findservice(s);
+ if (fd>=0) return fd;
+ if (chdir(initroot) || chdir(s)) return -1;
+#ifdef INIT_CREATE_INOUT_PIPE
+ if (SYS_mknod("log/in",S_IFIFO|0600,0)==0) symlink("log/in","out");
+#endif
+ byte_zero(&tmp, sizeof(tmp));
+ tmp.startedat = time(0);
+ tmp.pr_respawn = (access("respawn", R_OK)==0);
+ return addprocess(&tmp, s);
+}
+
+static void childhandler();
+static int do_poll(struct pollfd *pfd, int timeout) {
+ int k, n = timeout & 3;
+ for (k=0; k<n; k++) {
+ pfd[k].events = POLLIN;
+ pfd[k].revents = 0;
+ }
+ CHILD_UNBLOCK;
+ k=poll(pfd,n,timeout);
+ CHILD_BLOCK;
+ if (k<0) {
+ if (errno==EINTR) childhandler();
+#ifdef INIT_POLL_FAILED
+ else { write(1,"poll failed!\n",13); sulogin(); }
+#endif
+ }
+ return k;
+}
+
+static int startbyind(int service, int father) {
+ struct process *pr = root + service;
+ char *name = process_name(pr);
+ pid_t pid;
+ struct timespec req = {0,500000000};
+
+ if (pr->pid || pr->pr_circular) return service;
+ pr->pr_circular=1;
+ pr->father=father;
+ pr->cron=0;
+#if INIT_HISTORY > 0
+ for (pid = INIT_HISTORY; pid>1; --pid) history[pid] = history[pid-1];
+ history[1]=service+1;
+#endif
+ if (chdir(initroot)) return -1;
+ while ((pid=fork()) < 0) nanosleep(&req,0);
+ if (pid==0) {
+ CHILD_UNBLOCK;
+ if (chdir(initsysdir)==0) {
+ char *aa[5];
+ INIT_ARGS5(aa, pr->pr_end ? "end" : "./run",name,initroot,initsysdir,0);
+ if (time(0) == pr->startedat) nanosleep(&req,0);
+ free_execve("./run",aa,environ);
+ }
+ _exit(1);
+ }
+ pr->pid=pid;
+ pr->startedat=time(0);
+ if (chdir(name)==0) {
+ int fd;
+ if (pr->pr_end) pr->pr_end = 0;
+ else pr->pr_end = (access("end",X_OK)==0);
+
+ if ((fd=open("sync", O_RDONLY)) >= 0) {
+ struct pollfd pfd[2];
+ unsigned int u;
+ byte_zero(pfd, sizeof(pfd));
+ read(fd, pfd, sizeof(pfd)-1);
+ close(fd);
+
+ if ((u=atoulong((void *)pfd)) == 0) u = -1;
+ pfd->fd = -1;
+ do {
+ do_poll(pfd, 1001); /* must be 4k+1 */
+ if (pr->pid < 2) break;
+ } while (u--);
+ }
+ }
+ return service;
+}
+
+static int startbyname(char *name,int father) {
+ char *s;
+ int fd,len,service=loadservice(name);
+ if (service<0) return -1;
+ name = root_name(service);
+#ifdef INIT_BLACK
+ if (father>=0)
+ for (len=1; (s=Argv[len]); len++)
+ if (*s=='#' && !str_diff(s+1,name))
+ return service;
+#endif
+#ifdef INIT_LOG_SERVICE
+ len = str_len(name);
+ s = alloca(len+5); if (s==0) return -1;
+ byte_copy(s,len,name);
+ byte_copy(s+len,5,"/log");
+ startbyname(s,service);
+#endif
+
+ if (chdir(initroot) || chdir(name)) return -1;
+ if ((fd = open("depends", O_RDONLY)) >= 0) {
+ char *p, exitasapl=0;
+ if (GLOBAL_READ(fd,s, len,32000)) { close(fd); return 0; }
+ close(fd);
+ s[len]=0;
+ for (p=s; ;p=++s) {
+ while (*s && *s!='\n') s++;
+ if (*s==0) ++exitasapl;
+ *s=0;
+ startbyname(p,service);
+ if (exitasapl) break;
+ }
+ }
+ return startbyind(service,father);
+}
+
+/* return -1 on error */
+static int restartservice(char *s) {
+ int n=loadservice(s);
+ if (n>=0 && root[n].pid<2) {
+ if (start_time) root[n].cron = start_time;
+ else {
+ root[n].pid=0;
+ circsweep();
+ n=startbyname(s,-1);
+ }
+ }
+ start_time = 0;
+ return n;
+}
+
+static void handlekilled(int ind) {
+ struct process *pr = root + ind;
+ next_cron = 0;
+ pr->pr_finish = 1;
+ pr->pid = 0;
+ if (pr->pr_end || pr->pr_respawn) {
+ circsweep();
+ startbyind(ind,pr->father);
+ } else {
+ pr->startedat=time(0);
+ pr->pid += 1;
+ }
+}
+
+static void childhandler() {
+ int ind;
+ pid_t pid;
+ if (do_update) return;
+ while ((pid=waitpid(-1,0,WNOHANG))) {
+ if (pid < 0) {
+#ifdef INIT_EXIT_WARNING
+ static char saidso;
+ if (!saidso) { write(2,"all services exited.\n",21); ++saidso; }
+#endif
+ return;
+ }
+ for (ind=0; ind<=maxprocess; ind++)
+ if (root[ind].pid==pid) {
+ handlekilled(ind);
+ break;
+ }
+ }
+}
+
+static void do_initctl(struct pollfd *pfd) {
+ int fd;
+ if (restartservice("sysvinit") >= 0) {
+ if ((fd=open(INIT_FIFO,O_RDWR)) >= 0)
+ initctl=fd;
+ } else ++initctl;
+ pfd[1].fd = initctl;
+ fcntl(pfd[1].fd,F_SETFD,FD_CLOEXEC);
+}
+
+static void read_infd() {
+ char *b1, *other, *buf;
+ struct process *pr;
+ int idx, len, offset;
+ uint32t u32;
+
+ buf = read_alloca(384);
+ if (buf==0 || (len=read(infd,buf,380)) <2) return;
+
+ b1 = buf + 1;
+ buf[len] = 0; buf[len+1] = 0;
+ offset = str_len(buf) + 1;
+ other = buf + offset;
+ u32 = atoulong(other);
+ next_cron = 0;
+
+ switch (buf[0]) {
+ case 'D':
+ if (*b1=='M') t_write(mem.x,mem.a);
+ else {
+ if (*b1=='1') do_update = 1;
+ t_write(mem.x+mem.r, mem.a-mem.r); /* names + history */
+ t_write(root, mem.l); /* precess */
+ }
+ break;
+ case 'U': /* U\0argv[0] or Uservice\0 */
+ if (*b1==0) {
+ char *aa[3]; INIT_ARGS3(aa, other, "", 0);
+ execve(other,aa,environ);
+ } else
+ if (len >= offset + PROCESS_SIZE &&
+ addprocess(other,b1) >= 0) goto ok;
+ goto error;
+ case 's':
+ start_time = u32;
+ if (restartservice(b1) < 0) goto error;
+ok: t_write("1",1); break;
+error: t_write("0",1); break;
+
+ default:
+ if ((idx=findservice(b1)) <0) goto error;
+ pr = root + idx;
+
+ switch(buf[0]) {
+ case 'r': pr->pr_respawn = (other[0] != 0); goto write_pr;
+ case 'P':
+ if ((pid_t)u32<2 || kill(u32,0)) goto error;
+ pr->pid = u32; goto write_pr;
+ case 'c': pr->cron = u32;
+ case 'p':
+write_pr:
+ t_write(pr, sizeof(struct process));
+ }
+ }
+}
+
+int main(int argc, char *argv[]) {
+ struct pollfd pfd[12];
+ time_t now;
+ int i;
+#ifdef INIT_BLACK
+ Argv=argv;
+#endif
+ mem.r = INIT_ALLOC_BUFFER;
+ for (i=1; i<argc; i++)
+ if (argv[i][0]=='-') {
+ char *p = argv[i]+2;
+ switch (p[-1]) {
+ case 'M': mem.r = atoulong(p); break;
+#ifdef INIT_HOME
+ case 'H': initroot = p; break;
+ case 'S': initsysdir = p; break;
+#endif
+ default: p[-2] = '#'; /* -sshd --> #sshd */
+ }
+ }
+
+ mem.r &= 0xfff8; /* max 64K */
+ mem.a = mem.r + (INIT_HISTORY+1) * sizeof(unsigned short);
+ mem.x = alloca(mem.a); /* if it fails bummer */
+ history = mem.x + mem.r;
+ byte_zero(history, (INIT_HISTORY+1) * sizeof(unsigned short));
+ ((unsigned char*)history)[1] = INIT_HISTORY;
+
+ if (getpid()==1) {
+ set_reboot(0);
+ i = open("/dev/console",O_RDWR|O_NOCTTY);
+ if (i>0) { dup2(i,0); close(i); }
+ ioctl(0, KDSIGACCEPT, SIGWINCH);
+ }
+
+ set_sa(SIGCHLD);
+ set_sa(SIGINT); /* ctrl-alt-del */
+ set_sa(SIGWINCH); /* keyboard request */
+#ifdef INIT_SYSVINIT_SIGNALS
+ set_sa(SIGPWR); /* ifdef INIT_POWERSTATUS start powerZ */
+ set_sa(SIGHUP); /* ifdef INIT_SIGHUP start levelU */
+ set_sa(SIGUSR1); /* ifdef INIT_SIGUSR1 reopen /dev/initctl */
+#endif
+ CHILD_BLOCK;
+
+ if (chdir(initroot) ||
+ (infd=open("in",O_RDWR)) < 0 ||
+ (outfd=open("out",O_RDWR|O_NONBLOCK)) < 0) {
+#ifdef INIT_PIPES_ERROR
+ write(1,"pipes error\n",12); sulogin();
+#endif
+ } else {
+ int fd=open(".sync", O_RDONLY);
+ while ((i=read(fd,pfd,sizeof(pfd))) > 0) write(1,pfd,i);
+ close(fd);
+ }
+
+ fcntl(infd,F_SETFD,FD_CLOEXEC);
+ fcntl(outfd,F_SETFD,FD_CLOEXEC);
+
+ if (argv[argc-1][0] == 0) {
+ ++do_update;
+ t_write("8",1);
+ } else {
+ for (i=1; i<argc; i++) restartservice(argv[i]);
+ if (maxprocess < 0) restartservice("default");
+ }
+
+ pfd[0].fd = infd;
+ pfd[1].fd = -1;
+
+ for (;;) {
+ for (i=0; i<4; i++)
+ if (got_sig[i]) {
+ char *xx = NINIT_SERVICES_CODED;
+ got_sig[i]=0; restartservice(xx + xx[i]);
+ }
+ childhandler();
+
+ if ((time(&now)) > next_cron) {
+ time_t t; next_cron = now + 1800;
+ for (i=0; i<=maxprocess; i++) {
+ struct process *pr = root + i;
+ if (pr->pid > 1 && kill(pr->pid, 0)) handlekilled(i);
+
+ if (pr->pid < 2 && (t=pr->cron) > 0) {
+ if (t < now) restartservice(root_name(i));
+ if (t < next_cron) next_cron = t;
+ }
+ }
+ }
+
+ if ((i=do_poll(pfd, 5002)) == 0) { /* timeout; must be 4k+2 */
+ INIT_FREE_MEMBLOCKS(do_update=0);
+ if (initctl < -1) do_initctl(pfd);
+ continue;
+ }
+ if (i>0) {
+ if (pfd[0].revents) read_infd();
+ if (pfd[1].revents) restartservice("sysvinit");
+ }
+ }
+}
diff --git a/pidfile.c b/pidfile.c
new file mode 100644
index 0000000..d8e14e6
--- /dev/null
+++ b/pidfile.c
@@ -0,0 +1,65 @@
+#include <unistd.h>
+#include <errno.h>
+#include <alloca.h>
+#include <fcntl.h>
+#include "ninitfeatures.h"
+#include "process_defs.h"
+
+static int infd, outfd;
+
+#include "open_inout.h"
+#include "tryservice.h"
+
+int main(int argc, char* argv[]) {
+ char *x, *initroot, **arg;
+ struct process pr[6];
+ unsigned long pidfile;
+ int count=0, pid, ret=1;
+
+ if (argc<4) die(1,
+ "usage: ninit-pidfile",
+ " service /var/run/daemon.pid [-H home] /sbin/daemon ...");
+ errmsg_iam("ninit-pidfile");
+
+ if (unlink(argv[2]) && errno != ENOENT)
+ die(1, "could not remove pid file: ", argv[2]);
+
+ arg = argv + 3;
+ x = arg[0];
+
+ if (argc > 5 && x[0] =='-' && x[1] =='H' && x[2] ==0) {
+ initroot = arg[1];
+ arg += 2;
+ } else
+ initroot = INITROOT;
+
+ while ((pid = fork()) < 0) nano_sleep(0, 500000000); /* 0.5 sec */
+
+ if (pid == 0) {
+ x = arg[0];
+
+ arg[0] = x + str_rchr(x,'/');
+ if (arg[0]) arg[0]++;
+ else arg[0]=x;
+
+ execve(x,arg,environ);
+ die(3, "execve failed on: ", x);
+ }
+
+ while (1) {
+ if (read_ulongs(argv[2], &pidfile, 1)) {
+ if (str_len(argv[1]) > 320) ret=1;
+ else {
+ ret = tryservice(initroot,argv[1],"P",fu(pidfile), pr);
+ ret = (ret != sizeof(pr[0]) || pr->pid != (pid_t)pidfile);
+ }
+ break;
+ }
+ if (++count>=30) break;
+ else
+ nano_sleep(1, 0);
+ }
+
+ if (ret) carp("could not set PID of service ", argv[1]);
+ return ret;
+}
diff --git a/printf.c b/printf.c
new file mode 100644
index 0000000..1d14e54
--- /dev/null
+++ b/printf.c
@@ -0,0 +1,99 @@
+#include <unistd.h>
+#include <errno.h>
+
+struct mini_buffer {
+ char *x;
+ unsigned int p, n;
+ int fd;
+};
+
+static char buf_space[512];
+struct mini_buffer b = { buf_space, 0, sizeof(buf_space), 1 };
+
+static void s_write(int fd, char *s, int n) {
+ int w;
+ while (n) {
+ w = write(fd,s,n);
+ if (w==0) _exit(1);
+ if (w==-1) {
+ if (errno == EINTR) continue;
+ _exit(1);
+ }
+ s += w;
+ n -= w;
+ }
+}
+
+void out_flush() { s_write(b.fd, b.x, b.p); b.p=0; }
+void out_char(char ch) { if (b.p==b.n) out_flush(); b.x[b.p] = ch; b.p++; }
+void outs(char *s) { while (*s) { out_char(*s); ++s; } }
+
+static void bad(char *fmt, char *s) {
+ if (b.fd==1) out_flush();
+ b.fd=2;
+ outs("\nbad format: "); outs(fmt); outs("\nstart: ");
+ outs(s); outs("\n"); out_flush(); _exit(2);
+}
+
+char *conv_esc(char *x, char *pr_char) {
+ char *e0 = "ntrabfvEe\\";
+ char *e1 = "\n\t\r\a\b\f\v\033\033\\";
+ char ch;
+ int k;
+ for (k=0; k<10; k++)
+ if (e0[k] == *x)
+ { *pr_char = e1[k]; return x; }
+
+ for (k=0, ch=0; '0'<=*x && *x<'8' && k<3; k++, x++)
+ ch = ch*8 + (*x-'0');
+ if (k == 0) return 0;
+ *pr_char = ch;
+ return --x;
+}
+
+int main(int argc, char **argv) {
+ char *fmt, *s, *x, ch;
+ if (argc<2) _exit(100);
+ fmt = argv[1];
+ argv += 2;
+
+ for (s=fmt; (ch=*s); s++) {
+ if (ch != '%' && ch != '\\') goto print_char;
+ if ((ch = *++s) == 0) goto bad_fmt;
+
+ if (s[-1] == '\\') {
+ if ((x=conv_esc(s,&ch)) == 0) goto bad_fmt;
+ s = x;
+ print_char:
+ out_char(ch);
+ } else { /* % */
+ switch (ch) {
+ case '%': goto print_char;
+ case 's':
+ case 'b':
+ if ((x=*argv++)) {
+ if (ch=='s') outs(x);
+ else { /* %b */
+ char *y, *tmp;
+ for (tmp=x; (ch=*x); x++) {
+ if (ch == '\\') {
+ if ((y=conv_esc(++x,&ch)) == 0) bad(tmp, x-1);
+ x = y;
+ }
+ out_char(ch);
+ }
+ }
+ break;
+ }
+ out_flush(); b.fd=2;
+ outs("\nmissing argument");
+ default:
+ bad_fmt:
+ bad(fmt,s-1);
+ }
+ }
+ }
+ out_flush();
+ fsync(1);
+ _exit(close(1));
+}
diff --git a/procfs.c b/procfs.c
new file mode 100644
index 0000000..a48e074
--- /dev/null
+++ b/procfs.c
@@ -0,0 +1,84 @@
+#include <fcntl.h>
+#include <unistd.h>
+#include <alloca.h>
+#include "ninitfeatures.h"
+#include "nsvc_buffer.h"
+
+static buffer b = BUFFER_INIT(read, 0, 0, BUFFER_TMP_LEN);
+
+static void print_procfs(char *pidnum) {
+ char *x, *qq[8], *buf = b.x;
+ int fd,k;
+ unsigned char ch;
+
+ if (chdir("/proc/") || chdir(pidnum)) return;
+
+ INIT_ARGS4(qq, "cmdline","environ","status","statm");
+ for (k=0; k<4; k++) {
+ x = qq[k];
+
+ fd = open(x, O_RDONLY);
+ if (fd>=0) {
+ b.fd = fd;
+ fmt_color(x,'5',":");
+
+ if (k==2) {
+ int got=0, ok_line=0;
+ char first[24];
+
+ msg("\tcat /proc/", pidnum, "/status");
+ while (buffer_getc(&b, (char *)&ch)>0) {
+ if (got<3) { first[got++]=ch; continue; }
+ if (ok_line) outc(ch);
+ if (ch=='\n') { got=0; ok_line=0; continue; }
+
+ if (got>3) continue;
+ if (!str_diffn(first,"Uid",3) ||
+ !str_diffn(first,"Gid",3) ||
+ !str_diffn(first,"Gro",3) ||
+ !str_diffn(first,"Sta",3)) ok_line=1;
+ first[got]=ch;
+ first[++got]=0;
+ if (ok_line) outs(first);
+ }
+ } else { /* k=0,1 */
+ outc('\n');
+ while (buffer_getc(&b, (char *)&ch) >0) {
+ switch (ch) {
+ case 0: msg("\260"); break;
+ case 1: ch=':';
+ default:
+ if (ch != '\n' && (ch < 32 || ch > 127)) {
+ x = fu(ch); *--x = '.';
+ fmt_color(x,'1',"");
+ }
+ else outc(ch);
+ }
+ }
+ }
+ close(fd);
+ }
+ }
+
+ INIT_ARGS3(qq, "exe","cwd","root");
+ for (fd=0; fd<3; fd++) {
+ x = qq[fd];
+ k = readlink(x, buf, BUFFER_TMP_LEN);
+ if (k>0 && k < BUFFER_TMP_LEN) {
+ buf[k] = 0;
+ fmt_color(x,'5',":\t");
+ msg(buf);
+ }
+ }
+}
+
+int main(int argc, char **argv) {
+ if (argc>1) {
+ __b_1.x = alloca(BUFFER_1_LEN);
+ b.x = alloca(BUFFER_TMP_LEN);
+
+ print_procfs(argv[1]);
+ buffer_flush(&__b_1);
+ }
+ return 0;
+}
diff --git a/pututmpid.c b/pututmpid.c
new file mode 100644
index 0000000..84492c1
--- /dev/null
+++ b/pututmpid.c
@@ -0,0 +1,142 @@
+/* pututmpid.c
+
+ usage: pututmpid [-w] c2 child arg...
+ usage: pututmpid reboot
+ usage: pututmpid halt
+
+ c2 is the same entry as in /etc/inittab
+ c2:2345:respawn:/sbin/fgetty tty2
+ see the end of this file how it works
+*/
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <utmp.h>
+#include <time.h>
+#include <sys/utsname.h>
+#include "ninitfeatures.h"
+#include "utmp_defs.h"
+
+int main(int argc, char **argv) {
+ struct utmp u;
+ char *utid,*argv0,*x;
+ char flagwtmp=0;
+ int fd;
+ off_t pos=0;
+
+ if (argv[1] && argv[1][0] == '-' && argv[1][1] == 'w')
+ {++argv; --argc; flagwtmp=1;}
+
+ if (argc >=3) {
+ char **e, *env[2] = {0,0};
+ size_t len = str_len(argv[1]);
+ utid = argv[1];
+ if (len>4) utid += len-4;
+ argv += 2;
+
+ fd=open(_PATH_UTMP, O_RDWR);
+ while (utmp_io(fd, &u, F_RDLCK)) {
+ if (!str_diffn(u.ut_id, utid, sizeof(u.ut_id))) {
+ if (u.ut_type == USER_PROCESS) {
+ u.ut_tv.tv_sec=time(0);
+ u.ut_type=DEAD_PROCESS;
+ /* byte_zero(&u.ut_user, sizeof(u.ut_user));
+ byte_zero(&u.ut_host, sizeof(u.ut_host)); */
+ do_wtmp(&u);
+ }
+ break ;
+ }
+ pos += sizeof(struct utmp);
+ }
+
+ byte_zero(&u,sizeof(u));
+ str_copyn(u.ut_id, utid, sizeof(u.ut_id));
+ u.ut_pid=getpid();
+ u.ut_tv.tv_sec=time(0);
+ u.ut_type=INIT_PROCESS;
+
+ if (fd>=0 && lseek(fd,pos,SEEK_SET) == pos)
+ utmp_io(fd,&u,F_WRLCK);
+ close(fd);
+
+ if (flagwtmp) do_wtmp(&u);
+
+ argv0 = argv[0];
+ x = argv[0];
+ x += str_rchr(x,'/');
+ if (x[0]) argv[0] = x+1;
+ else argv[0] = argv0;
+
+ for (e=environ; *e; e++)
+ if (!str_diffn(*e,"TERM=",5)) {*env=*e; break;}
+
+ execve(argv0, argv, env);
+ write(2,"unable to exec: ",16);
+ write(2,argv0,str_len(argv0));
+ write(2,"\n",1);
+ return(100);
+ }
+
+ /******************************************************/
+ if (argc == 2) {
+ struct utsname uname_buf;
+ fd = -1;
+ switch (argv[1][0]) {
+ case 'r':
+ fd=open(_PATH_UTMP, O_RDWR);
+ while (utmp_io(fd, &u, F_RDLCK)) {
+ /* if already exist do nothing */
+ if (u.ut_type == BOOT_TIME) { close(fd); return 111; }
+ pos += sizeof(struct utmp);
+ }
+
+ byte_zero(&u,sizeof(u));
+ str_copy(u.ut_user,"reboot");
+ str_copy(u.ut_line,"~");
+ u.ut_type=BOOT_TIME;
+ break;
+ case 's':
+ case 'h':
+ str_copy(u.ut_user,"shutdown");
+ u.ut_type=RUN_LVL;
+ str_copy(u.ut_line,"~~");
+ break;
+ default:
+ goto usage;
+ }
+
+ str_copy(u.ut_id,"~~");
+ u.ut_pid=0;
+ u.ut_tv.tv_sec=time(0);
+
+ /* Put the OS version in place of the hostname */
+ if (uname(&uname_buf) == 0)
+ str_copyn(u.ut_host, uname_buf.release, 32); /* XXX overflow ? */
+
+ if (fd>=0 && lseek(fd,pos,SEEK_SET) == pos)
+ utmp_io(fd,&u,F_WRLCK);
+ close(fd);
+
+ do_wtmp(&u);
+ return(0);
+ }
+
+ usage:
+ write(2,"usage: pututmpid [-w] ut_id child arg...\n\
+ pututmpid reboot\n\
+ pututmpid halt\n",87);
+ return(100);
+}
+
+/*
+ pututmpid:
+ if exist an entry in utmp with: ut_id=c2 ut_type=USER_PROCESS
+ then put: ut_id=c2 ut_type=DEAD_PROCESS in wtmp
+
+ always put: ut_id=c2 ut_type=INIT_PROCESS in utmp
+ if -w flag put: ut_id=c2 ut_type=INIT_PROCESS in wtmp
+
+ getty and login chanegs /var/run/utmp:
+ getty: ut_id=c2 ut_type=LOGIN_PROCESS
+ login: ut_id=c2 ut_type=USER_PROCESS
+*/
diff --git a/reboot.c b/reboot.c
new file mode 100644
index 0000000..5cfbec2
--- /dev/null
+++ b/reboot.c
@@ -0,0 +1,38 @@
+#include <unistd.h>
+#include <sys/reboot.h>
+#include "ninitfeatures.h"
+#include "uid.h"
+
+#define USAGE "ninit-reboot: Aborted.\nSay \"ninit-reboot (RESTART|ENABLE_CAD|DISABLE_CAD|HALT|POWER_OFF)\" if you really mean it.\n"
+
+void usage(void) {
+ write(2, USAGE, str_len(USAGE));
+ _exit(1);
+}
+
+int main(int argc, char *argv[]) {
+ unsigned int m=0;
+ if (argc!=2)
+ usage();
+
+ sync();
+ sync();
+ sync();
+ nano_sleep(1, 0);
+
+ if (!str_diff(argv[1], "RESTART")) m=RB_AUTOBOOT;
+ else if (!str_diff(argv[1], "HALT")) m=RB_HALT_SYSTEM;
+ else if (!str_diff(argv[1], "ENABLE_CAD")) m=RB_ENABLE_CAD;
+ else if (!str_diff(argv[1], "DISABLE_CAD")) m=RB_DISABLE_CAD;
+ else if (!str_diff(argv[1], "POWER_OFF")) m=RB_POWER_OFF;
+ else usage();
+
+ set_reboot(m);
+ set_reboot(RB_HALT_SYSTEM);
+
+ close(0);
+ close(1);
+ close(2);
+
+ while(1) nano_sleep(10,0);
+}
diff --git a/reload.c b/reload.c
new file mode 100644
index 0000000..7d6821a
--- /dev/null
+++ b/reload.c
@@ -0,0 +1,321 @@
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/poll.h>
+#include <limits.h>
+#include "ninit.h"
+
+#define USAGE "Usage:\n" \
+"\tninit-reload ","-v","\n" \
+"\tninit-reload ","-m > dump_memory_file\n" \
+"\tninit-reload ","-d > dump_data_file\n" \
+"\tninit-reload ","[Options] /path/to/ninit [arg(s)...]\n", \
+"\nOptions:\n\t[-t time_diff]\t[-f data_file]\t" \
+"[-u]\t\t[-v]\n\t" \
+"[-R service]\t[-r number]\t[-a auto-size]\n\t" \
+"[-E file]\t[-e var=name]\t[-e variable]\t[-e -]\n"
+
+#define UNKNOWN_PROCESS_SIZE
+
+static char *buf, *flag_file;
+static unsigned int verbose;
+static char do_update, get_memory, get_data;
+
+#define UL sizeof(uint32t)
+
+void die_xx(char *s) { errmsg_iam("ninit-reload"); err(2,s,0); _exit(1); }
+
+static int safe_read_code(char code) {
+ int r, k;
+ for (k=0; k<50; k++) {
+ r = read(outfd,buf,BUFFER_TMP_LEN);
+ if (r == 1 && buf[0] == code) return 0;
+ if (r <= 0) nano_sleep(0, 20000000); /* 0.020 sec */
+ else break;
+ }
+ return -1;
+}
+static void safe_read() {
+ if (safe_read_code('1')) die_xx("wrong responce from ninit");
+}
+static void safe_read_execve(char code, char *s) {
+ if (safe_read_code(code) == 0) return;
+ die(1, "Ops! I got exec error from ",s);
+}
+static void safe_write(int len) {
+ if (len != write(infd, buf, len)) die_xx("error writing to ninit");
+}
+static int safe_cat(char *dest, char *src, int len) {
+ int k = str_len(dest);
+ if (k<len) k += str_copyn(dest+k, src, len-k);
+ dest[k] = 0;
+ return k;
+}
+static void put_arg(char *string) {
+ uint32t *len = (uint32t *)buf;
+ buf[UL] = 0;
+ *len = safe_cat(buf+UL, string, 300);
+ safe_write(*len + UL);
+ safe_read();
+}
+
+#include "check_opt.h"
+#include "get_services.h"
+#include "open_inout.h"
+
+static char *do_AutoSize(uint32t len) {
+ uint32t u;
+ char *x;
+ if (len>300) len=300;
+ u = (maxprocess + len + 8) * (PROCESS_SIZE + 12);
+
+ x = fu(u & ~15);
+ *--x = 'M'; *--x = '-';
+
+#ifndef INIT_TIMEOUT_WRITE
+ if (u > PIPE_BUF - 256)
+ carp("\n\a*** WARNING *** for option ",x, " ninit must be build\n"
+ "with INIT_TIMEOUT_WRITE flag. See ninitfeatures.h");
+#endif
+ return x;
+}
+
+static char *align_name(char *s) {
+ static char x[20];
+ int len = str_len(s);
+ byte_set(x,sizeof(x),' ');
+ if (len<=16) len=18 - len;
+ else { len=1; x[0] = '\t'; }
+ x[len] = 0;
+ return x;
+}
+
+int main(int argc, char **Argv) {
+ char *remove_P[argc], *env_c[argc], **env=0;
+ char *auto_memory=0, **argv;
+ time_t TimeDiff=0;
+ int do_Remove=0, do_remove=0, remove_p[argc], AutoSize=0;
+ int i,j,k,env_len=0;
+ uint32t *len;
+ char *initroot, *x;
+
+ INIT_ENV_GET_HOME(initroot,"NINIT_HOME","INIT_HOME");
+ buf = alloca(BUFFER_TMP_LEN);
+ len = (uint32t *)buf;
+
+ mem.a = INIT_ALLOC_BUFFER;
+ if (argc < 2) { usage: die(1, USAGE); }
+ if (initroot == 0) initroot = INITROOT;
+ argv=Argv;
+
+ argv++;
+ errmsg_iam("ninit-reload");
+ while (argv[0] && argv[0][0]=='-') {
+ switch(argv[0][1]) {
+ case 'v': verbose++; break;
+ case 'u': do_update=1; break;
+ case 'm': get_memory=1; break;
+ case 'd': get_data=1; break;
+ case 'f': chk_opt(argv,x); flag_file = x; break;
+ case 't': chk_opt(argv,x); TimeDiff = x_atoi(x); break;
+ case 'a': chk_opt(argv,x); AutoSize = x_atoi(x); break;
+ case 'R': chk_opt(argv,x); remove_P[do_Remove++] = x; break;
+ case 'e': chk_opt(argv,x);
+ /* next clears all like env -i! to remove '-' do;
+ * echo > /tmp/x; ninit-raload -E /tmp/x ... */
+ if (x[0]=='-' && x[1]==0) x="";
+ env_c[env_len++] = x; break;
+ case 'E': chk_opt(argv,x);
+ i=open(x, O_RDONLY);
+ if (i<0) die(1, "unable to open ", x);
+ else {
+ if (GLOBAL_READ(i,x, j,12000)) _exit(1);
+ close(i);
+ x[j] = 0;
+
+ j = splitmem(0,x,'\n');
+ env = alloca((argc + j + 5) * sizeof(char *));
+ if (env == 0) _exit(1);
+ splitmem(env,x,'\n');
+ skip_comments(env);
+ }
+ break;
+ case 'r':
+ chk_opt(argv,x);
+ if ('0'<=x[0] && x[0]<='9') remove_p[do_remove++] = x_atoi(x);
+ break;
+ default:
+ die(1, "Unknown Option: ",argv[0]);
+ }
+ argv++;
+ }
+
+ argc -= (argv-Argv);
+
+ x = env_get("NINIT_MEMORY");
+ if (x) mem.a = atoulong(x);
+ mem.r=mem.a;
+ if (mem.a==0 || mem.a > 64000 || (mem.x=alloca(mem.a))==0) _exit(1);
+
+ if (env==0) env=env_c;
+ else {
+ char **xx=env+1, *s;
+ for (k=1; (s=*xx); xx++) if (*s) env[k++] = s;
+ byte_copy(env+k, env_len * sizeof(char *), env_c);
+ env_len += k;
+ }
+
+ if (flag_file==0) open_inout(initroot);
+ else {
+ if (*flag_file == 0) goto usage;
+ infd=-1;
+ outfd=open(flag_file, O_RDONLY);
+ if (outfd<0) die(1, "could not open ",flag_file);
+ process_size = lseek(outfd,0,SEEK_END);
+ lseek(outfd,0,SEEK_SET);
+ }
+
+ errmsg_iam(0);
+ if (get_memory || get_data) {
+ struct pollfd pfd;
+ pfd.fd=outfd;
+ pfd.events=POLLIN;
+ buf[0] = 'D';
+ buf[1] = (get_memory) ? 'M' : '1';
+ write(infd,buf,2);
+
+ for (;;) {
+ j=poll(&pfd,1,400);
+ if (j==-1) {
+ if (errno==EINTR) { carp("interrupt signal"); }
+ else die_xx("poll fialed!");
+ } else if (j==1) {
+ i=read(outfd,buf,BUFFER_TMP_LEN);
+ if (i>0) write(1,buf,i);
+ } else {
+ return 0;
+ }
+ }
+ }
+
+ get_services();
+
+ if (verbose) {
+ char *what;
+ msg("services from: ",
+ (flag_file) ? "" : initroot,
+ (flag_file) ? flag_file : "/out");
+ for (j=0; j<= maxprocess; j++) {
+ switch (root[j].pid) {
+ case 1: what = "finish"; break;
+ case 0: what = "down"; break;
+ default: what = fu(root[j].pid);
+ }
+ x = root_name(j);
+ msg(fu(j),".\t",x,align_name(x),what);
+ }
+ if (!do_update) return 0;
+ }
+
+ if (do_update) {
+ if (!argv || !argv[0] || argv[0][0]!='/')
+ die_xx("I need the full /path/of/ninit/program");
+
+ k = str_len(argv[0]);
+ if (k > 240) die_xx("too long file name. must be <= 240.");
+
+ if (access(argv[0], X_OK))
+ die(1, "Ops! Check the mode of ", argv[0]);
+
+ if (AutoSize) auto_memory=do_AutoSize(AutoSize);
+
+ errmsg_puts(1,"\nreplacing current ninit with: ");
+ fmt_argv(1,argv, " ");
+ msg("",auto_memory);
+
+ if (flag_file) {
+ close(infd); close(outfd);
+ open_inout(initroot);
+ }
+
+ buf[0]='U'; buf[1]='U'; buf[2]=0;
+
+ if (argc>1 || auto_memory || env_len) {
+ /* ----- to ninit ----- */
+ safe_cat(buf, initroot, 200);
+ k = safe_cat(buf, "/sys/update", 230);
+ if (access(buf+2, X_OK))
+ die(1, "Ops! Check the mode of ", buf+2);
+
+ buf[1] = 0;
+ buf[k++] = 0;
+ if (verbose) msg("sending reload signal to ","ninit");
+ safe_write(k);
+
+ /* ----- from/to update ----- */
+ safe_read_execve('7', "~/sys/update");
+ *len=argc + 1 + (auto_memory != 0);
+ safe_write(UL);
+ safe_read();
+
+ for (i=0; i<argc; i++) put_arg(argv[i]); /* argv */
+ if (auto_memory) put_arg(auto_memory);
+ put_arg("");
+
+ *len=env_len;
+ safe_write(UL);
+ safe_read();
+ for (i=0; i<env_len; i++) put_arg(env[i]);
+
+ *len = 1953066852;
+ if (verbose) msg("sending reload signal to ","~/sys/update","\n");
+ safe_write(UL);
+ } else {
+ k=safe_cat(buf, argv[0], 300);
+ buf[1] = 0;
+ buf[k++] = 0;
+ if (verbose) msg("sending reload signal to ","ninit","\n");
+ safe_write(k); /* execve argv[0] */
+ }
+
+ /* ----- from/to ninit ----- */
+ safe_read_execve('8',argv[0]);
+ if (verbose) msg("now trying to restore services:");
+
+ for (j=0; j<= maxprocess; j++) {
+ char *name=root_name(j);
+
+ for (k=0; k<do_remove; k++)
+ if (j==remove_p[k]) {
+ if (verbose) { msg("\t",name,align_name(name),"removed"); }
+ k=-1; break;
+ }
+ if (k==-1) continue;
+
+ for (k=0; k<do_Remove; k++)
+ if (!str_diff(remove_P[k],name)) {
+ if (verbose) { msg("\t",name,align_name(name),"removed"); }
+ k=-1; break;
+ }
+ if (k==-1) continue;
+
+ root[j].pr_circular = 0;
+ root[j].startedat += TimeDiff;
+ buf[0]='U'; buf[1]=0;
+ k = 1 + safe_cat(buf, name, 300);
+ byte_copy(buf + k, PROCESS_SIZE, &root[j]);
+ k += PROCESS_SIZE;
+
+ safe_write(k);
+ safe_read();
+ if (verbose) { msg(fu(j),".\t",name,align_name(name),"done"); }
+ }
+
+ if (verbose) errmsg_puts(1,"\n");
+ msg("Done!\tTry: nsvc -L");
+ return(0);
+ }
+ goto usage;
+ return 0;
+}
diff --git a/remove.c b/remove.c
new file mode 100644
index 0000000..e3d0db3
--- /dev/null
+++ b/remove.c
@@ -0,0 +1,74 @@
+#include <unistd.h>
+#include <alloca.h>
+#include <fcntl.h>
+#include "ninitfeatures.h"
+/*
+ convert: xxx option A B C D
+ to: /sbin/ninit-reload arg(s)
+ and start above program
+ options: -V -E -Z[number]
+ */
+int main(int argc, char **argv) {
+ char *x, **qq, **q;
+ char *help_file = ".sync";
+ char flag[3]={ '-', 'e', 0 };
+
+
+ x = argv[1];
+ if (x == 0) { help_file = ".nsvc_help"; goto write_it; }
+ if (x[0] != '-') _exit(1);
+
+ qq = alloca(sizeof(char *) * (2*argc + 30));
+ q = qq;
+
+ INIT_ARGS2(q, "/sbin/ninit-reload","-u");
+ q += 2;
+
+ switch (x[1]) {
+ case 'Z': flag[1] = 'R';
+ case 'E':
+ if (x[2]) {
+ x[1]='a';
+ INIT_ARGS2(q,x,"-v");
+ q += 2;
+ }
+ break;
+ case 'V': {
+ char buf[1024];
+ int r, fd;
+
+ write_it:
+ fd = open(help_file, O_RDONLY);
+ while ((r = read(fd, buf, sizeof(buf))) > 0) /* close(fd); */
+ write(1, buf, r);
+ }
+ default:
+ _exit(0);
+ }
+
+ for (argv += 2; (x=*argv); argv++) {
+ if (*x == 0) continue; /* skip empty args */
+ INIT_ARGS2(q,flag,x);
+ q += 2;
+ }
+
+ *q++ = "/sbin/ninit";
+ INIT_ENV_GET_HOME(x,"NINIT_HOME","INIT_HOME");
+
+ if (x) {
+ x -= 2;
+ x[0] = '-';
+ x[1]='H';
+ *q++ = x;
+ }
+ *q = 0;
+
+ carp("NINIT-remove: starting:");
+ fmt_argv(1, qq, " ");
+ errmsg_puts(1,"\n");
+ errmsg_puts(1,0);
+
+ /* return 0; */
+ execve(qq[0], qq, environ);
+ _exit(1);
+}
diff --git a/run.c b/run.c
new file mode 100644
index 0000000..7a642ad
--- /dev/null
+++ b/run.c
@@ -0,0 +1,177 @@
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <alloca.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#include <grp.h>
+#include "ninitfeatures.h"
+#include "uid.h"
+
+static char *service, *home, *sysdir;
+
+static void exec_setup(char *s) {
+ if (access(s, X_OK) == 0) {
+ char *x = fu(getpid());
+ pid_t pid=fork();
+ if (pid==-1) return;
+ if (pid==0) {
+ char *qq[6];
+ INIT_ARGS6(qq, s,service,home,sysdir,x,0);
+ execve(s,qq,environ);
+ _exit(1);
+ }
+ waitpid(pid,0,0);
+ }
+}
+
+static struct error_table table[] = {
+ {ENOEXEC, "Exec format error"},
+ {ENOENT, "No such file"},
+ {EACCES, "Permission denied"},
+ {ENOMEM, "Out of memory"},
+ {0,0}
+};
+
+static void die_exec() {
+ char *x=0, *y = ": ";
+ struct error_table *t=table;
+ for (; t->s; t++)
+ if (t->n == errno) { x = t->s; break; }
+ if (x==0) { x = fu(errno); y = ": errno="; }
+ errmsg_iam(0);
+ carp(home,"/",sysdir,"/run",": Could not start ",
+ home,"/",service,"/run",y,x);
+ _exit(1);
+}
+
+#define NI_TEXT_FILES 2
+#define MAX_groups 33
+enum { NI_params,NI_pidfile };
+
+int main(int Argc, char **Argv) {
+ char **argv,*argv_0,**xx,*s;
+ char *tmp[10];
+ static char **aa[NI_TEXT_FILES];
+ static unsigned long uid[MAX_groups + 2];
+ unsigned long ul[1];
+ int i,fd,len;
+
+ errmsg_iam("run");
+ if (Argc<3)
+ { carp("usage: ",errmsg_argv0," service home sys"); _exit(1); }
+
+ service=Argv[1];
+ home= Argv[2];
+ sysdir= Argv[3];
+
+ if (chdir(home) || chdir(service)) die_exec();
+
+ if (Argv[0][0] != '-') {
+ if (getppid() == 1) {
+ ioctl(0, TIOCNOTTY, 0);
+ setsid();
+ opendevconsole();
+ // ioctl(0, TIOCSCTTY, 1);
+ // tcsetpgrp(0, getpgrp());
+ }
+
+ if (Argv[0][0] == 'e') { /* "end" */
+ execve(Argv[0],Argv,environ);
+ _exit(100);
+ }
+
+ exec_setup("sys-rsetup");
+
+ if (!access("wait", R_OK) ||
+ !access("environ", R_OK) ||
+ !access("softlimit", R_OK) ||
+ !access("cron", R_OK)) {
+ Argv[0] = "run-wait";
+
+ if (chdir(home) || chdir(sysdir)) return 1;
+ execve("./run-wait",Argv, environ);
+ if (chdir(home) || chdir(service)) return 1; /* exec fails; go on */
+ }
+ }
+
+ exec_setup("rsetup");
+
+ INIT_ARGS2(tmp, "params","pidfile");
+ for (i=0; i < NI_TEXT_FILES; i++) {
+ int offset=6;
+ if (i) offset=0;
+
+ if ((fd = open(tmp[i], O_RDONLY)) >=0) {
+ if (GLOBAL_READ(fd,s, len,12000)) _exit(1);
+ close(fd);
+ s[len]=0;
+
+ len = splitmem(0,s,'\n');
+ xx=alloca((len+offset+1) * sizeof(char*)); if (xx==0) _exit(1);
+ xx += offset;
+ aa[i] = xx;
+
+ splitmem(xx,s,'\n');
+ if (i==0 && xx[--len][0]==0) xx[len]=0;
+ skip_comments(xx);
+ }
+ }
+
+ if ((xx=aa[NI_params])) argv = xx;
+ else { argv = tmp+7; *argv = 0; }
+
+ for (len=129; ; len *= 2) {
+ argv_0=alloca(len); if (!argv_0) _exit(1);
+ if ((i=readlink("run",argv_0,len))<0) {
+ if (errno!=EINVAL) die_exec(); /* not a symbolic link */
+ argv_0="./run";
+ break;
+ }
+ if (i<len) { argv_0[i]=0; break; }
+ }
+
+ if ((xx=aa[NI_pidfile])) {
+ argv -= 5;
+ INIT_ARGS5(argv, service, xx[0], "-H", home, argv_0);
+ argv_0 = "/sbin/ninit-pidfile";
+ }
+
+ s = argv_0 + str_rchr(argv_0, '/');
+ if (s[0]) ++s;
+ else s = argv_0;
+ *--argv = s;
+
+ dup2_inout("in", 0,O_RDONLY);
+ dup2_inout("out",1,O_WRONLY);
+
+ if (read_ulongs("sleep",ul,1))
+ nano_sleep(*ul, 200000000);
+ if ((s=read_header("nice"))) {
+ if (*s == '-') { i=-1; s++; } else i=1;
+ if (scan_ulong(s, ul)) { i *= (int)ul[0]; nice(i); }
+ }
+
+ len = read_ulongs("uid",uid,33);
+#ifdef RUN_WANT_GID_FILE
+ if (len < 2 && read_ulongs("gid",uid+1,1)) len=2;
+#endif
+
+ if (len > 1) {
+ gid__t g[len];
+ for (i=0; i<len; i++) g[i] = uid[i];
+ SYS_setgroups(len-1,g+1);
+ }
+ if (uid[1]) setgid(uid[1]);
+ if (uid[0]) setuid(uid[0]);
+
+ exec_setup("setup");
+ if (read_ulongs("alarm",ul,1)) alarm(*ul);
+
+ for (fd=3; fd<1024; ++fd) close(fd);
+
+ if (!access("pause", R_OK)) kill(getpid(), SIGSTOP);
+ execve(argv_0,argv,environ);
+ die_exec();
+ return 1;
+}
diff --git a/runlevel.c b/runlevel.c
new file mode 100644
index 0000000..cf9930b
--- /dev/null
+++ b/runlevel.c
@@ -0,0 +1,137 @@
+#include <unistd.h>
+#include <utmp.h>
+#include <time.h>
+#include <alloca.h>
+#include <fcntl.h>
+#include <sys/utsname.h>
+#include "ninitfeatures.h"
+#include "utmp_defs.h"
+
+static unsigned long env_free, env_allocated;
+#include "contrib/put_env.h"
+
+static void init_utmp(struct utmp *u) {
+ byte_zero(u, sizeof(struct utmp));
+ str_copy(u->ut_line,"~");
+ str_copy(u->ut_id,"~~");
+ u->ut_tv.tv_sec=time(0);
+}
+
+int main(int argc, char **argv) {
+ pid_t prev=0;
+ int i,fd;
+ char **env, ch, prevlevel[20], ninit_runlevel[20];
+ char boot_time=0;
+ struct utmp u;
+ struct utsname uname_buf;
+ off_t pos=0, runlevel_pos=0;
+
+ str_copy(prevlevel, "PREVLEVEL=N");
+ str_copy(ninit_runlevel, "NINIT_RUNLEVEL=N");
+
+ fd=open(_PATH_UTMP, (argc>1) ? O_RDWR : O_RDONLY);
+ while (utmp_io(fd, &u, F_RDLCK)) {
+ if (u.ut_type == BOOT_TIME) boot_time = 1;
+ if (u.ut_type == RUN_LVL) {
+ prev = u.ut_pid;
+ runlevel_pos = pos;
+
+ if (argc < 2) {
+ char m[] = "N N\n";
+ i = prev / 256; if (i) m[0] = i;
+ i = prev % 256; if (i) m[2] = i;
+ write(1, m, 4);
+ return 0;
+ }
+ }
+ pos += sizeof(struct utmp);
+ }
+
+ if (argc < 2) _exit(1);
+ prev %= 256;
+ ch = argv[1][0];
+
+ if (boot_time == 0) {
+ init_utmp(&u);
+ u.ut_type=BOOT_TIME;
+ str_copy(u.ut_user,"reboot");
+ if (lseek(fd,pos,SEEK_SET) == pos)
+ utmp_io(fd,&u,F_WRLCK);
+ }
+
+ pos = runlevel_pos;
+ if (pos == 0 && lseek(fd,0,SEEK_SET) == 0) {
+ while (utmp_io(fd, &u, F_RDLCK)) {
+ if (u.ut_type == RUN_LVL) break;
+ pos += sizeof(struct utmp);
+ }
+ }
+
+ init_utmp(&u);
+ u.ut_type=RUN_LVL;
+ str_copy(u.ut_user,"runlevel");
+ if (prev==0) prev='N';
+ u.ut_pid = (256*prev) + ch;
+
+ if (lseek(fd,pos,SEEK_SET) == pos)
+ utmp_io(fd,&u,F_WRLCK);
+ close(fd);
+
+ if (uname(&uname_buf) == 0)
+ str_copyn(u.ut_host, uname_buf.release, 32); /* XXX overflow ? */
+ do_wtmp(&u);
+
+ /* ------ exnivon + exeve ----- */
+ if (environ==0) environ = argv+argc;
+ for (env=environ; *env; ++env) env_allocated++;
+
+ env_free = argc + 12;
+ env=alloca((env_allocated + env_free + 1) * sizeof(char*));
+ if (env==0) return -1;
+ byte_copy(env, (env_allocated+1)*sizeof(char *), environ);
+ environ = env;
+
+ prevlevel[10] = prev;
+ ninit_runlevel[15] = ch;
+
+ put_env(prevlevel);
+ put_env(ninit_runlevel);
+ put_env(ninit_runlevel+6);
+ put_env("INIT_VERSION=" SYSVINIT_VERSION);
+ put_env("CONSOLE=/dev/console");
+ put_env("PATH=/bin:/usr/bin:/sbin:/usr/sbin");
+
+ for (i=2; i<argc; ++i) {
+ char *v=argv[i];
+ char *nenv=0;
+ if (v[0]=='-') {
+ if (v[1]==0) {
+ environ[0]=0;
+ } else {
+ switch (*++v) {
+ case 'i': environ[0]=0; goto do_it;
+ case 'u':
+ if (v[1]) { nenv = v+1; goto do_it; }
+ else if (argv[++i]) { nenv = argv[i]; goto do_it; }
+ default:
+ goto ready;
+ }
+ }
+ } else {
+ if (v[str_chr(v,'=')] == 0) goto ready;
+ nenv = v;
+ }
+
+ do_it:
+ if (nenv) put_env(nenv);
+ continue;
+ }
+
+ ready:
+ argv += i;
+ if (argv[0]) {
+ execve(argv[0], argv, environ);
+ return 100;
+ }
+ return 0;
+}
diff --git a/scripts/conf b/scripts/conf
new file mode 100755
index 0000000..b443af9
--- /dev/null
+++ b/scripts/conf
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+if test "$1" = "" ; then
+ echo usage: $0 /dir/name
+ exit 100
+fi
+
+mkdir -m 755 $1 || exit 1
+cd $1 || exit 2
+
+mkfifo -m 600 in || exit 3
+mkfifo -m 600 out || exit 4
+
+ln -s /etc/ninit/sys sys || exit 5
+ln -s /etc/ninit/.sync .sync || exit 6
+ln -s /etc/ninit/.nsvc_help .nsvc_help || exit 7
+
+ls -laF .
diff --git a/scripts/scan b/scripts/scan
new file mode 100755
index 0000000..aef0bab
--- /dev/null
+++ b/scripts/scan
@@ -0,0 +1,34 @@
+#!/bin/sh
+## *setup: $1=service S2=ninit_home $3=ninit_sys $4=pid
+## end: $1=service S2=ninit_home $3=ninit_sys
+
+op=''
+case $1 in
+ -u) op=u; shift; break;;
+ -o) op=o; shift; break;;
+ -d) op=d; shift; break;;
+esac
+
+if test $# -lt 2 ; then
+ echo 'usage: ninit-scan [ -[uod] ] service ninit_home'
+ exit 1
+fi
+
+cd $2 || exit 2
+
+if test "$1" = "" ; then
+ prefix=''
+else
+ cd $1 || exit 3
+ prefix=$1/
+fi
+
+for d in * ; do
+ if test -d $d -a -k $d ; then
+ x=$op
+ if test "$x" = "" ; then
+ test -f $d/respawn && x=u || x=o
+ fi
+ /bin/nsvc -$x $prefix$d
+ fi
+done
diff --git a/scripts/service b/scripts/service
new file mode 100755
index 0000000..0255308
--- /dev/null
+++ b/scripts/service
@@ -0,0 +1,104 @@
+#!/bin/sh
+
+if test -z "$1" ; then
+ printf "%s\n\n" '
+usage: ninit-service [-Options] service(s)
+usage: ninit-service -E service file
+
+Options:
+ -A ascii output
+ -C show end of lines; for cat
+ -L long lines; for ls
+ -E edit file; default editor: /bin/vi; change it with:
+ echo /usr/bin/emacs -nw > /etc/ninit/.editor
+ -H/other/home default home: /etc/ninit
+
+example: ninit-service `find /etc/ninit/ -type d`'
+ exit 1
+fi
+
+ lsopt=
+catopt=
+ home="/etc/ninit"
+editor="/bin/vi"
+cstart="\033\133\061\073\063\065\155"
+ cend="\033\133\060\073\063\071\155"
+
+
+while true ; do
+ case $1 in
+ -E) shift;
+ [ -f $home/.editor ] && editor=`head -1 $home/.editor`
+ [ -z "$editor" ] && editor='/bin/vi'
+ test $# -ge 2 && exec $editor $home/$1/$2
+ continue;;
+ -C*) catopt=-E ; shift; continue;;
+ -L*) lsopt=-l; shift; continue;;
+ -H*) home=`echo $1 | sed -e 's/^..//'`; shift; continue;;
+ -A*) cstart=""; cend=""; shift; continue;;
+ *) break ;;
+ esac
+done
+
+while true; do
+
+cd $home || exit 1
+[ -z "$1" ] && exit 0
+
+if ! cd $1 2>/dev/null ; then
+ printf "\n\n*** WARNING ***\t$cstart%s$cend is not a service\n\n" $1
+ shift
+ continue
+fi
+
+printf "\n\tservice: $cstart%s$cend\n" $1
+
+if [ -d log -a ! -p log/in ] ; then
+ printf "\n*** WARNING *** %s$cstart%s$cend %s\n" \
+ "you have not a FIFO " "log/in"
+ printf "%s\n\011%s\n\011%s\n\n" "create it with:" \
+ "cd $home/$1" "mkfifo -m 600 log/in && ln -s log/in out"
+fi
+
+ls -F $lsopt
+printf "\n"
+
+#### links
+for f in in out log run end; do
+ [ -e $f ] && printf "%s:\t" $f && ls -F -l $f | sed -s 's/ */ /g'
+done
+###
+
+### options
+for f in respawn pause pause-wait ; do
+ [ -f $f ] && printf "option:\t%s\n" $f
+done
+
+### first word
+for f in nice uid gid sleep maxwait sync alarm pidfile \
+ sysvinit-timeout; do
+ if [ -f $f ] ; then
+ read ans < $f
+ printf "%s:\t%s\n" $f $ans
+ fi
+done
+
+### text files:
+for f in params environ softlimit wait cron depends; do
+ if [ -f $f ] ; then
+ printf "\n$cstart%s$cend: ==>\n" $f
+ cat $catopt $f
+ printf '<==\n'
+ fi
+done
+
+### dirs:
+for f in depends.dir; do
+ if [ -d $f ] ; then
+ printf "\n$cstart%s$cend:\n" $f
+ ls -la $f
+ fi
+done
+
+shift
+done
diff --git a/scripts/tests.sh b/scripts/tests.sh
new file mode 100755
index 0000000..f07d807
--- /dev/null
+++ b/scripts/tests.sh
@@ -0,0 +1,126 @@
+#!/bin/sh
+export PATH=/bin:/usr/bin:/sbin
+
+if [ "$UID" = "0" ] ; then
+ echo '**** WARNING **** Do not start this script as ROOT !!!'
+ echo 'Please, interrupt me! Otherwise I continue in 30 sec.'
+ sleep 30
+fi
+
+h=`pwd`
+home=$h/home
+export TIMEFORMAT=' elapsed time: %R'
+export NINIT_HOME=$home
+
+cstart='' ; cend='' ; bad=''
+tty -s 2>/dev/null
+if [ $? -eq 0 ] ; then
+ # 30=black 31=red 32=green 33=yellow 34=blue ... 37=white
+ # 40=black 41=red ... backgrownd color
+ cstart='' ; cend=''; bad=''
+fi
+
+echo ' starting server' $cstart$h/ninit -H$home$cend
+echo ' wait for this test more than 25 seconds'
+echo ' no PANIC! the test terminates in 92 sec always'
+echo
+
+start_time=`date +%s`
+
+print_env () {
+ if [ -f /proc/$ninit_pid/environ ] ; then
+ echo
+ echo ' '$1 $cstart \
+ `./sleeprun -a4 tr '\000' ' ' < /proc/$ninit_pid/environ` $cend
+ fi
+}
+
+do_it () {
+ X=$1; shift
+ echo ' starting '$cstart$@$cend' #' $X
+ ./sleeprun -a20 $@
+ ret=$?
+ if [ $ret -ne 0 ] ; then
+ echo $bad'***** WARNING *****'$cend
+ echo 'Exit status of' $cstart$@$cend is $ret
+ sleep 3
+ fi
+ return $ret
+}
+
+do_it '(install HOME)' ./install-bin home < misc/HOME || exit 1
+test -f home/env/run || gzip -dc home.tar.gz | do_it '(unpack home)' tar -xv
+
+args="./sleeprun -a90 ./env -i NINIT_TIME=empty $h/ninit -H$home"
+echo ' starting' $cstart$args$cend
+$args &
+ninit_pid=$!
+echo ' ninit PID='$cstart$ninit_pid$cend
+
+do_it '(waiting the ninit to start)' sleep 1
+do_it '(list)' ./nsvc -L
+time do_it '(wait 4s env)' ./nsvc -W4 env
+do_it '(once)' ./nsvc -o sleep
+do_it '(set respawning mode)' ./nsvc -R sleep
+do_it '(list)' ./nsvc -L
+
+sleep 1
+do_it '(diagnostic)' ./nsvc sleep ; echo
+do_it '(list)' ./nsvc -L
+do_it '(list pids)' ./nsvc -g sleep default
+do_it '(history)' ./nsvc -H
+
+echo ; echo ' reloading ninit'
+
+after=after-reload
+do_it '' ps uwwww -p $ninit_pid ; echo
+do_it '' ln -sf ninit $after
+
+print_env 'old ninit environ:'
+
+current_time=`date +%s`
+args="./reload -v -a 20 -eNINIT_TIME=$current_time -e Hello=world"
+args="$args -eYou_are=$USER -u $h/$after -H$home"
+echo ' starting' $cstart$args$cend
+time ./sleeprun -a30 $args
+
+print_env 'new ninit environ:'
+
+do_it '(sleep 4+3 sec; stopping do_update mode)' sleep 4
+do_it '' ps uwwww -p $ninit_pid ; echo
+do_it '' sleep 3
+
+echo
+time do_it 'sync mode (max 3 sec)' ./nsvc -o S
+do_it '(list)' ./nsvc -L
+all=`./nsvc -L`
+do_it '(remove cron flags)' ./nsvc -K0 $all
+do_it '(down all services)' ./nsvc -d $all
+
+time do_it '(wait 1s all to finish)' ./nsvc -W1 $all
+do_it '(list)' ./nsvc -L
+do_it '(memory usage)' ./nsvc -D
+do_it '(depends)' ./nsvc -D default
+
+[ -f /proc/$ninit_pid/statm ] && \
+ do_it '(ninit daemon)' cat /proc/$ninit_pid/statm
+do_it '(kill ninit daemon)' kill $ninit_pid
+wait
+
+end_time=`date +%s`
+./sleeprun -a4 rm -f $after
+
+n=`expr $end_time - $start_time` ; echo
+echo ' test continues:' $cstart$n sec$cend
+
+echo
+echo $cstart' Creating services in ./etc/ninit - Demo '$cend
+do_it '(clean old ./etc/)' rm -r -f ./etc
+do_it '(install ./etc/ninit/)' ./install-bin . < misc/ETC
+do_it '(converter)' ./inittab /etc/inittab etc/ninit services.sh > /dev/null
+do_it '(making services - demo)' ./services.sh
+# do_it '' ls -F ./etc/ninit
+
+echo
+echo Look now at $cstart./services.sh$cend and $cstart./etc/ninit/$cend.
+echo
diff --git a/scripts/update.sh b/scripts/update.sh
new file mode 100755
index 0000000..c2ca22d
--- /dev/null
+++ b/scripts/update.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+# $1 = $(DESTDIR)
+
+D=$1
+[ "$D" = "/" ] && D=
+
+echo
+echo I will try to overwrite $D/sbin/ninit
+echo If PID 1 is $D/sbin/ninit I will get probably an error
+
+[ -w $D/sbin/ninit ] || chmod 755 $D/sbin/ninit 2>/dev/null
+./install-bin $D/sbin 'c:::755:/:ninit::' && exit 0
+
+echo I got an error. I suppose PID 1 is $D/sbin/ninit
+echo
+echo I will try to replace $D/sbin/ninit with `pwd`/ninit
+echo Please wait at least 30 seconds
+
+./sleeprun -a10 $D/sbin/ninit-reload -d > ninit.data && \
+ ./sleeprun -a5 $D/sbin/ninit-reload -f ninit.data -u `pwd`/ninit && \
+ ./install-bin $D/sbin 'c:::755:/:ninit::' && \
+ ./sleeprun -a8 $D/sbin/ninit-reload -u $D/sbin/ninit && \
+ exit 0
+
+echo 'All fails! I will try simply ./install-bin'
+
+./install-bin $D/sbin 'x:::755:/:ninit::'
diff --git a/shutdown.c b/shutdown.c
new file mode 100644
index 0000000..520acdb
--- /dev/null
+++ b/shutdown.c
@@ -0,0 +1,303 @@
+/*
+ * Notes:
+ * - uncomment `#define USE_INIT' below if you want to use shutdown
+ * with ninit. If defined, shutdown will try to bring down the services
+ * halt (for -h or -o) or reboot (-r) before killing all other processes.
+ * - If you do not use ninit shutdown will bring your system down similar
+ * to SysV-Inits shutdown with -n
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/reboot.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <time.h>
+#include <alloca.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <stdarg.h>
+#include "ninitfeatures.h"
+#include "uid.h"
+#include "check_opt.h"
+#define Y(...) exec_cmd(__VA_ARGS__, 0)
+
+#define USE_INIT
+
+static char *me;
+static int cfg_verbose;
+
+void sighandler(int sig) { (void)sig; }
+
+static void exec_argv(char **argv) {
+ execve(argv[0], argv, environ);
+ errmsg_iam(me);
+ carp("execve ", argv[0]," failed");
+}
+
+static void exec_cmd(char *cmd, ...) {
+ char *argv[25], **arg=argv; /* XXX can overflow */
+ const char *s;
+ va_list a;
+ pid_t pid;
+
+ *arg++ = cmd;
+ va_start(a,cmd);
+ do {
+ s=va_arg(a,const char*);
+ *arg++ = (char *)s;
+ } while (s);
+
+ while ((pid = fork()) < 0) nano_sleep(0,500000000);
+ if (pid == 0) {
+ exec_argv(argv);
+ _exit(0);
+ } else
+ while (pid != waitpid(-1,0,0)); /* also collect zombies */
+}
+
+static void deep_sleep(int t) {
+ time_t deedline = time(0) + t;
+ if (t<=0) return;
+ if (cfg_verbose) msg("sleeping ", fu(t), "...");
+ while (1) {
+ nano_sleep(1,0);
+ if (time(0) >= deedline) break;
+ }
+}
+
+static void die_xx(char *s) { errmsg_iam(me); carp(s); _exit(1); }
+
+#ifdef USE_INIT
+#include "ninit.h"
+
+static int infd, outfd;
+static char *buf, do_update, *nsvc_other;
+static unsigned int cfg_twice;
+static pid_t my_pid;
+
+#include "get_services.h"
+#include "open_inout.h"
+#undef CLEAN_SERVICE
+#include "tryservice_nsvc.h"
+
+static void try_srv(char *service, char *type) {
+ if (tryservice(service, type[0]))
+ carp("Could not ", type, " service: ", service);
+}
+
+void ninit_servicesDown(char **skip) {
+ int idx;
+ for (idx=0; idx<=maxprocess; idx++) {
+ char *what = "not running";
+ pid_t pid = root[idx].pid;
+ char *name = root_name(idx);
+
+ if (root[idx].pr_respawn) try_srv(name, "respawn off");
+ if (root[idx].cron) {
+ nsvc_other = "0";
+ try_srv(name, "cron off");
+ nsvc_other = 0;
+ }
+ if (pid > 1) {
+ char **x;
+ for (x=skip; *x; x++)
+ if (!str_diff(*x, name)) { pid=my_pid; what="skipped"; break; }
+
+ if (my_pid != pid) {
+ what = "done";
+ if (kill(pid, SIGTERM) == 0) kill(pid, SIGCONT);
+ else what = "failed";
+ }
+ }
+ if (cfg_verbose) msg("\t--> ", name, "\t\t", what);
+ }
+}
+
+void ninit_shutdown(unsigned int twice, char **skip) {
+ char *x;
+ INIT_ENV_GET_HOME(x,"NINIT_HOME","INIT_HOME");
+ if (x==0) x=INITROOT;
+ again:
+ open_inout(x);
+ msg("Shutting down ninit services");
+
+ get_services();
+ ninit_servicesDown(skip);
+
+ if (twice) {
+ close(infd);
+ close(outfd);
+ mem.l = 0;
+ mem.r = mem.a;
+ root = 0;
+ maxprocess = -1;
+ deep_sleep(twice);
+ twice = 0;
+ goto again;
+ }
+}
+#endif
+
+static void printUsage() {
+ carp("usage: ",me," -[rhoqvt"
+#ifdef USE_INIT
+ "mST"
+#endif
+ "] [-E program [arg(s)]]\n"
+ "\t-r:\treboot after shutdown\n"
+ "\t-h:\thalt after shutdown\n"
+ "\t-o:\tpower-off after shutdown\n"
+ "\t-q:\tquiet mode; ignores SIGINT signal\n"
+#ifdef USE_INIT
+ "\t-m:\tonly shutdown the ninit part\n"
+#endif
+ "\t-v:\tbe verbose\n"
+ "\t-E prog:\texecute prog after KILLALL;"
+ " must be last option!\n"
+ "\t-s secs:\tstarting delay\n"
+ "\t-t secs:\tdelay between SIGTERM and SIGKILL\n"
+#ifdef USE_INIT
+ "\t-T secs:\tif secs is nonzero shutdown twice ninit part\n"
+ "\t-S abcd:\tskip to shutdown the service abcd\n"
+#endif
+ );
+ _exit(1);
+}
+
+int main(int argc, char **argv) {
+ char *x;
+ unsigned int cfg_downlevel=2; /* 0:1:2 == reboot:halt:poweroff */
+ unsigned int cfg_delay = 3;
+ static unsigned int cfg_sleep;
+ static int cfg_ninitonly, cfg_quiet;
+ static char **prog;
+
+#ifdef USE_INIT
+ static char **skip, **skp;
+ skip = alloca((argc+5) * sizeof(char *));
+ skp = ++skip;
+
+ mem.a = INIT_ALLOC_BUFFER;
+ buf = alloca(BUFFER_TMP_LEN);
+#endif
+
+ if (getuid() != 0) die_xx("You are not root, go away!");
+ me = argv[0];
+ if (argc<2) printUsage();
+
+ /* parse commandline options */
+ argv++;
+ while (argv[0] && argv[0][0]=='-') {
+ char *y=argv[0] + 1;
+ for (; *y; y++) {
+ switch(*y) {
+ case 'q': /* ignore SIGINT */ ++cfg_quiet; break;
+ case 'v': /* verbose mode */ ++cfg_verbose; break;
+ case 'r': /* reboot */ cfg_downlevel = 0; break;
+ case 'h': /* halt */ cfg_downlevel = 1; break;
+ case 'o': /* poweroff */ cfg_downlevel = 2; break;
+ case 't': /* delay between SIGTERM and SIGKILL */
+ chk_opt(argv,x); cfg_delay = x_atoi(x); goto again;
+ case 's': /* sleep before shutdown services */
+ chk_opt(argv,x); cfg_sleep = x_atoi(x); goto again;
+#ifdef USE_INIT
+ case 'm': /* exit after ninit down */
+ cfg_ninitonly = 1; break;
+ case 'T': /* delay between two services down */
+ chk_opt(argv,x); cfg_twice = x_atoi(x); goto again;
+ case 'S': /* skip services */
+ chk_opt(argv,x); *skp++ = x; goto again;
+ case 'E': /* execve prog arg(s) */
+ chk_opt(argv,x); prog = argv; *prog = x; goto last_option;
+#endif
+ default:
+ printUsage();
+ }
+ }
+ again:
+ argv++;
+ }
+
+#ifdef USE_INIT
+ last_option:
+ *skp = 0;
+ *--skip = cfg_downlevel ? "halt" : "reboot";
+#endif
+
+ opendevconsole();
+
+ if (cfg_ninitonly == 0) {
+ switch (cfg_downlevel) {
+ case 0: x="reboot"; break;
+ case 1: x="halt"; break;
+ case 2: x="power-off";
+ }
+ msg(me, "\007: System is going down for ",x);
+ }
+ sync();
+
+ /* catch some signals; getting killed after killing the controlling terminal
+ * wouldn't be such a great thing...
+ */
+ set_sa(SIGQUIT); set_sa(SIGCHLD); set_sa(SIGHUP );
+ set_sa(SIGTSTP); set_sa(SIGTTIN); set_sa(SIGTTOU);
+
+ if (cfg_quiet) set_sa(SIGINT);
+ deep_sleep(cfg_sleep);
+
+#ifdef USE_INIT
+ x = env_get("NINIT_MEMORY");
+ if (x) mem.a = atoulong(x);
+ mem.r=mem.a;
+ if (mem.a==0 || mem.a > 64000 || (mem.x=alloca(mem.a))==0) _exit(1);
+ my_pid = getpid();
+
+ sync();
+
+ ninit_shutdown(cfg_twice, skip);
+ if (cfg_ninitonly == 0 && prog == 0) {
+ msg("Starting service: ", *skip);
+ try_srv(*skip, "start");
+ }
+ close(infd);
+ close(outfd);
+ sync();
+
+ if (cfg_ninitonly) {
+ if (prog) exec_argv(prog);
+ _exit(0);
+ }
+ deep_sleep(cfg_delay);
+#endif
+
+ /* kill all processes still left */
+ sync();
+ msg("Sending all processes the ", "TERM signal...");
+ kill(-1, SIGTERM);
+ deep_sleep(cfg_delay);
+
+ sync();
+ msg("Sending all processes the ", "KILL signal...");
+ kill(-1, SIGKILL);
+
+ if (prog) { sync(); exec_argv(prog); }
+ sync();
+
+ /* maximal 20 arguments !!! */
+ if (!access("/sbin/quotaoff", X_OK)) Y("/sbin/quotaoff", "-a");
+ Y("/sbin/swapoff", "-a"); sync();
+ Y("/bin/umount", "-a");
+ Y("/bin/mount", "-o", "remount,ro", "/");
+ sync();
+
+ /* and finally reboot, halt or power-off the system */
+ switch (cfg_downlevel) {
+ case 0: cfg_downlevel = RB_AUTOBOOT; break;
+ case 1: cfg_downlevel = RB_HALT_SYSTEM; break;
+ case 2: cfg_downlevel = RB_POWER_OFF;
+ }
+
+ set_reboot(cfg_downlevel);
+ return 0;
+}
diff --git a/sighandler.h b/sighandler.h
new file mode 100644
index 0000000..6a81f6c
--- /dev/null
+++ b/sighandler.h
@@ -0,0 +1,26 @@
+static volatile char got_sig[4];
+
+void sighandler(int sig) {
+ if (sig == SIGCHLD) return;
+#ifdef NINIT_SIGNAL_HANDLER_CODED
+ {
+ unsigned char *Sig = (unsigned char *)NINIT_SIGNAL_HANDLER_CODED;
+ int k;
+ for (k=0; k<4; k++)
+ if ((unsigned char)sig == Sig[k])
+ { got_sig[k] = 1; return; }
+ }
+#else
+
+ if (sig == SIGWINCH) got_sig[0]=1;
+ if (sig == SIGINT) got_sig[1]=1;
+#ifdef INIT_SYSVINIT_SIGNALS
+ if (sig == SIGPWR) got_sig[2]=1;
+ if (sig == SIGHUP) got_sig[3]=1;
+#endif
+#endif /* NINIT_SIGNAL_HANDLER_CODED */
+
+#ifdef INIT_SYSVINIT_SIGNALS
+ if (sig == SIGUSR1) { close(initctl); initctl = -5; }
+#endif
+}
diff --git a/softlimit.c b/softlimit.c
new file mode 100644
index 0000000..fb3233f
--- /dev/null
+++ b/softlimit.c
@@ -0,0 +1,116 @@
+#include <sys/types.h>
+#include <sys/resource.h>
+#include "ninitfeatures.h"
+
+static char *arg;
+
+static void doit(int resource)
+{
+ unsigned long u;
+ struct rlimit r;
+
+ if (getrlimit(resource,&r) == -1) {
+ carp("g","etrlimit failed");
+ return;
+ }
+
+ if (arg[0]=='=' && arg[1]==0)
+ r.rlim_cur = r.rlim_max;
+ else {
+ if (arg[scan_ulong(arg,&u)]) {
+ carp("invalid line: ", arg-1);
+ return;
+ }
+ r.rlim_cur = u;
+ if (r.rlim_cur > r.rlim_max)
+ r.rlim_cur = r.rlim_max;
+ }
+
+ if (setrlimit(resource,&r) == -1)
+ carp("s","etrlimit failed");
+}
+
+void softlimit(char **argv) /*EXTRACT_INCL*/ {
+ while (*argv) {
+ arg = argv[0] + 1;
+ switch(argv[0][0]) {
+ case 0: break;
+ case 'a':
+#ifdef RLIMIT_AS
+ doit(RLIMIT_AS);
+#endif
+#ifdef RLIMIT_VMEM
+ doit(RLIMIT_VMEM);
+#endif
+ break;
+ case 'c':
+#ifdef RLIMIT_CORE
+ doit(RLIMIT_CORE);
+#endif
+ break;
+ case 'd':
+#ifdef RLIMIT_DATA
+ doit(RLIMIT_DATA);
+#endif
+ break;
+ case 'f':
+#ifdef RLIMIT_FSIZE
+ doit(RLIMIT_FSIZE);
+#endif
+ break;
+ case 'l':
+#ifdef RLIMIT_MEMLOCK
+ doit(RLIMIT_MEMLOCK);
+#endif
+ break;
+ case 'm':
+#ifdef RLIMIT_DATA
+ doit(RLIMIT_DATA);
+#endif
+#ifdef RLIMIT_STACK
+ doit(RLIMIT_STACK);
+#endif
+#ifdef RLIMIT_MEMLOCK
+ doit(RLIMIT_MEMLOCK);
+#endif
+#ifdef RLIMIT_VMEM
+ doit(RLIMIT_VMEM);
+#endif
+#ifdef RLIMIT_AS
+ doit(RLIMIT_AS);
+#endif
+ break;
+ case 'o':
+#ifdef RLIMIT_NOFILE
+ doit(RLIMIT_NOFILE);
+#endif
+#ifdef RLIMIT_OFILE
+ doit(RLIMIT_OFILE);
+#endif
+ break;
+ case 'p':
+#ifdef RLIMIT_NPROC
+ doit(RLIMIT_NPROC);
+#endif
+ break;
+ case 'r':
+#ifdef RLIMIT_RSS
+ doit(RLIMIT_RSS);
+#endif
+ break;
+ case 's':
+#ifdef RLIMIT_STACK
+ doit(RLIMIT_STACK);
+#endif
+ break;
+ case 't':
+#ifdef RLIMIT_CPU
+ doit(RLIMIT_CPU);
+#endif
+ break;
+ default:
+ carp("invalid line: ", argv[0]);
+ }
+ argv++;
+ }
+}
diff --git a/struct_root.h b/struct_root.h
new file mode 100644
index 0000000..21e3257
--- /dev/null
+++ b/struct_root.h
@@ -0,0 +1,33 @@
+/*
+ if you have problems with time define
+ startedat and cron bellow to be time_t instead of int32t
+*/
+
+union pr_flags {
+ struct {
+ char b0 : 1;
+ char b1 : 1;
+ char b2 : 1;
+ } b;
+ char ch;
+};
+
+#define pr_respawn pr_flags.b.b0
+#define pr_end pr_flags.b.b1
+#define pr_finish pr_flags.b.b2
+
+#define INIT_ROOT_DEFINE(Process,Type) \
+struct Process {\
+ Type name;\
+ pid_t pid;\
+ union pr_flags pr_flags;\
+ char pr_circular;\
+ unsigned short father;\
+ int32t startedat;\
+ int32t cron;\
+}
+
+struct memalloc {
+ void *x;
+ uint32t l,r,a;
+};
diff --git a/system/Files b/system/Files
new file mode 100644
index 0000000..6b1aaad
--- /dev/null
+++ b/system/Files
@@ -0,0 +1,55 @@
+# -*-Makefile-*-
+CC = gcc
+CFLAGS = -Os -W -Wall
+
+ifeq ($(FLAG_DEBUG),no)
+CCC_ = @echo ' CC $<';
+else
+CCC_ =
+endif
+
+CCC = $(CCC_) $(CC) $(CFLAGS) $(OPTIMIZATION)
+
+NINIT_O=fork.o waitpid.o __waitpid.o wait4.o \
+ nanosleep.o time.o __time.o gettimeofday.o \
+ close.o open.o getpid.o access.o execve.o ioctl.o write.o \
+ dup2.o fcntl.o poll.o chdir.o kill.o read.o lseek.o mmap.o \
+ munmap.o symlink.o rt_sigaction.o rt_sigprocmask.o \
+ SYS_reboot.o SYS_mknod.o
+
+RUN_O=setuid.o setgid.o SYS_setgroups.o \
+ writev.o getppid.o setsid.o \
+ alarm.o __alarm.o settimer.o \
+ readlink.o nice.o __nice.o getpriority.o setpriority.o
+
+RUN_WAIT_O=setrlimit.o getrlimit.o flock.o
+NSVC_O=getuid.o geteuid.o
+OTHER = unlink.o sync.o rename.o pipe.o uname.o \
+ SYS_chown.o chmod.o mkdir.o umask.o fsync.o \
+ __errno.o __environ.o __errno_location.o SYS_brk.o __sbrk.o
+
+ALL = $(NINIT_O) $(RUN_O) $(RUN_WAIT_O) $(NSVC_O) $(OTHER)
+
+start.o: system.a
+
+__%.o: ../__%.c
+ $(CCC) -c -o $@ $<
+
+start.o: start.S
+ $(CCC) -c -include ../features.h $<
+unified.o: unified.S
+ $(CCC) -c -include ../features.h $<
+%.o: %.S
+ $(CCC) -c -include ./syscalls.h $<
+
+SYS_%.S:
+ @( echo '#ifdef __NR_'$*; \
+ echo 'syscall_weak('$*,SYS_$*,$*')'; \
+ echo '#endif' ) > $@
+%.S:
+ @( echo '#ifdef __NR_'$*; \
+ echo 'syscall('$*,$*')'; \
+ echo '#endif' ) > $@
+
+clean:
+ rm -rf *.o *.a SYS_*.S
diff --git a/system/README b/system/README
new file mode 100644
index 0000000..7fda65a
--- /dev/null
+++ b/system/README
@@ -0,0 +1,5 @@
+This is part of dietlibc.
+I wrote only Makefile, Files and *.c.
+*.S are almost copy/paste.
+
+Nikola
diff --git a/system/__alarm.c b/system/__alarm.c
new file mode 100644
index 0000000..49eb8f6
--- /dev/null
+++ b/system/__alarm.c
@@ -0,0 +1,17 @@
+#include <asm/unistd.h>
+#ifndef __NR_alarm
+
+#include <unistd.h>
+#include <sys/time.h>
+
+unsigned int alarm(unsigned int seconds) {
+ struct itimerval old, new;
+ unsigned int ret;
+ new.it_interval.tv_usec=0;
+ new.it_interval.tv_sec=0;
+ new.it_value.tv_usec =0;
+ new.it_value.tv_sec =(long)seconds;
+ if (setitimer(ITIMER_REAL,&new,&old)==-1) return 0;
+ return old.it_value.tv_sec+(old.it_value.tv_usec?1:0);
+}
+#endif
diff --git a/system/__environ.c b/system/__environ.c
new file mode 100644
index 0000000..b6c6a47
--- /dev/null
+++ b/system/__environ.c
@@ -0,0 +1 @@
+char **environ=0;
diff --git a/system/__errno.c b/system/__errno.c
new file mode 100644
index 0000000..3c6cdb0
--- /dev/null
+++ b/system/__errno.c
@@ -0,0 +1 @@
+int errno=0;
diff --git a/system/__errno_location.c b/system/__errno_location.c
new file mode 100644
index 0000000..76900d3
--- /dev/null
+++ b/system/__errno_location.c
@@ -0,0 +1,3 @@
+extern int errno;
+int *__errno_location() { return &errno; }
+
diff --git a/system/__nice.c b/system/__nice.c
new file mode 100644
index 0000000..7898012
--- /dev/null
+++ b/system/__nice.c
@@ -0,0 +1,8 @@
+#include <asm/unistd.h>
+#include <sys/resource.h>
+
+#ifndef __NR_nice
+int nice(int i) {
+ return setpriority(PRIO_PROCESS,0,getpriority(PRIO_PROCESS,0)+i);
+}
+#endif
diff --git a/system/__sbrk.c b/system/__sbrk.c
new file mode 100644
index 0000000..df2a302
--- /dev/null
+++ b/system/__sbrk.c
@@ -0,0 +1,25 @@
+#include <sys/types.h>
+#define N ((void *)-1)
+
+extern void *SYS_brk(void *x);
+static void *cur = N;
+
+void *sbrk(ssize_t incr) {
+ void *t=0, *old=N;
+
+ if (cur == N) {
+ again:
+ cur = SYS_brk(t);
+ if (cur == N) return cur;
+ }
+
+ if (old == N) {
+ old = cur;
+ if (incr) {
+ t = old + incr;
+ goto again;
+ }
+ }
+
+ return old;
+}
diff --git a/system/__time.c b/system/__time.c
new file mode 100644
index 0000000..2ade864
--- /dev/null
+++ b/system/__time.c
@@ -0,0 +1,14 @@
+#include <asm/unistd.h>
+
+#ifndef __NR_time
+
+#include <time.h>
+#include <sys/time.h>
+
+time_t time(time_t *foo) {
+ struct timeval tv;
+ gettimeofday(&tv,0);
+ if (foo) *foo=tv.tv_sec;
+ return tv.tv_sec;
+}
+#endif
diff --git a/system/__waitpid.c b/system/__waitpid.c
new file mode 100644
index 0000000..06f44c1
--- /dev/null
+++ b/system/__waitpid.c
@@ -0,0 +1,11 @@
+#include <asm/unistd.h>
+
+#ifndef __NR_waitpid
+
+#include <sys/types.h>
+#include <sys/wait.h>
+
+pid_t waitpid(pid_t pid, int *status, int options) {
+ return wait4(pid, status, options, 0);
+}
+#endif
diff --git a/system/features.h b/system/features.h
new file mode 100644
index 0000000..507b5ff
--- /dev/null
+++ b/system/features.h
@@ -0,0 +1,7 @@
+/* use __errno_location instead of errno */
+#define WANT_THREAD_SAFE
+
+/* on i386, Linux has an alternate syscall method since 2002/12/16 */
+/* on my Athlon XP, it is twice as fast, but it's only in kernel 2.5 */
+/* 20040118: enabling this breaks User Mode Linux! It's their fault. */
+#define WANT_SYSENTER
diff --git a/system/i386/Flags b/system/i386/Flags
new file mode 100644
index 0000000..77b27ee
--- /dev/null
+++ b/system/i386/Flags
@@ -0,0 +1 @@
+OPTIMIZATION = -fomit-frame-pointer -mpreferred-stack-boundary=2 -falign-functions=1 -falign-jumps=1 -falign-loops=1
diff --git a/system/i386/Makefile b/system/i386/Makefile
new file mode 100644
index 0000000..f02a48f
--- /dev/null
+++ b/system/i386/Makefile
@@ -0,0 +1,5 @@
+include Flags
+include ../Files
+
+system.a: unified.o unified_256.o $(ALL) __restore.o __restore_rt.o
+ ar cr $@ $^
diff --git a/system/i386/__restore.S b/system/i386/__restore.S
new file mode 100644
index 0000000..2c7936a
--- /dev/null
+++ b/system/i386/__restore.S
@@ -0,0 +1,12 @@
+#include "syscalls.h"
+
+.text
+.type __restore,@function
+.global __restore
+.align 8
+__restore:
+ popl %eax
+ movl $__NR_sigreturn,%eax
+ int $0x80
+ hlt /* die if syscall returns */
+.size __restore,.-__restore
diff --git a/system/i386/__restore_rt.S b/system/i386/__restore_rt.S
new file mode 100644
index 0000000..06bb240
--- /dev/null
+++ b/system/i386/__restore_rt.S
@@ -0,0 +1,9 @@
+.text
+.type __restore_rt,@function
+.global __restore_rt
+.align 8
+__restore_rt:
+ movl $__NR_rt_sigreturn,%eax
+ int $0x80
+ hlt /* die if syscall returns */
+.size __restore_rt,.-__restore_rt
diff --git a/system/i386/mmap.S b/system/i386/mmap.S
new file mode 100644
index 0000000..c2b375e
--- /dev/null
+++ b/system/i386/mmap.S
@@ -0,0 +1,11 @@
+.text
+.global mmap
+.type mmap,@function
+mmap:
+ mov $__NR_mmap,%al
+ lea 0x4(%esp,1),%edx
+ push %edx
+ call __unified_syscall
+ pop %ecx
+ ret
+.size mmap,.-mmap
diff --git a/system/i386/start.S b/system/i386/start.S
new file mode 100644
index 0000000..762e807
--- /dev/null
+++ b/system/i386/start.S
@@ -0,0 +1,37 @@
+.text
+.global _start
+_start:
+ popl %ecx /* %ecx = argc */
+ movl %esp,%esi /* %esi = argv */
+ pushl %ecx
+ leal 4(%esi,%ecx,4),%eax /* %eax = envp = (4*ecx)+%esi+4 */
+
+ pushl %eax
+ pushl %esi
+ pushl %ecx
+ movl %eax,environ
+
+#ifdef WANT_SYSENTER
+ movl %eax,%edx
+ xorl %esi,%esi
+1:
+ add $4,%edx
+ cmpl %esi,-4(%edx)
+ jne 1b
+1:
+ movl (%edx),%edi
+ test %edi,%edi
+ jz 1f
+ addl $8,%edx
+ cmpl $32,%edi
+ jne 1b
+ movl -4(%edx),%edi
+ movl %edi,__vsyscall
+1:
+#endif
+
+ call main
+ pushl %eax
+ call exit
+ hlt /* die now ! will ya ... */
+.size _start,.-_start
diff --git a/system/i386/syscalls.h b/system/i386/syscalls.h
new file mode 100644
index 0000000..41c21db
--- /dev/null
+++ b/system/i386/syscalls.h
@@ -0,0 +1,34 @@
+#include <asm/unistd.h>
+
+#define syscall(name,sym) \
+.text; \
+.type sym,@function; \
+.global sym; \
+sym: \
+.ifle __NR_##name-255; \
+ movb $__NR_##name,%al; \
+ jmp __unified_syscall; \
+.else; \
+ movw $__NR_##name,%ax; \
+ jmp __unified_syscall_256; \
+.endif; \
+.Lend##sym: ; \
+.size sym,.Lend##sym-sym
+
+#define syscall_weak(name,wsym,sym) \
+.text; \
+.type wsym,@function; \
+.weak wsym; \
+wsym: ; \
+.type sym,@function; \
+.global sym; \
+sym: \
+.ifle __NR_##name-255; \
+ movb $__NR_##name,%al; \
+ jmp __unified_syscall; \
+.else; \
+ movw $__NR_##name,%ax; \
+ jmp __unified_syscall_256; \
+.endif; \
+.Lend##sym: ; \
+.size sym,.Lend##sym-sym
diff --git a/system/i386/unified.S b/system/i386/unified.S
new file mode 100644
index 0000000..82f8636
--- /dev/null
+++ b/system/i386/unified.S
@@ -0,0 +1,68 @@
+#ifdef WANT_SYSENTER
+.data
+.type __vsyscall,@object
+.global __vsyscall
+__vsyscall:
+.Lvsyscall:
+.long .Lcallint80
+#endif
+
+.text
+.weak exit
+exit:
+.global _exit
+.type _exit,@function
+_exit:
+ movb $1,%al
+.size _exit,.-_exit
+
+.global __unified_syscall
+.type __unified_syscall,@function
+__unified_syscall:
+ movzbl %al, %eax
+.size __unified_syscall,.-__unified_syscall
+
+.global __unified_return
+.type __unified_return,@function
+__unified_return:
+ push %edi
+ push %esi
+ push %ebx
+ movl %esp,%edi
+ /* we use movl instead of pop because otherwise a signal would
+ destroy the stack frame and crash the program, although it
+ would save a few bytes. */
+ movl 0x10(%edi),%ebx
+ movl 0x14(%edi),%ecx
+ movl 0x18(%edi),%edx
+ movl 0x1c(%edi),%esi
+ movl 0x20(%edi),%edi
+#ifdef WANT_SYSENTER
+ call *.Lvsyscall /* 0xffffe000 */
+#else
+ int $0x80
+#endif
+ cmp $-124,%eax
+ jb .Lnoerror
+ neg %eax
+#ifdef WANT_THREAD_SAFE
+ movl %eax,%ebx
+ call __errno_location
+ movl %ebx,(%eax)
+ orl $-1,%eax
+#else
+ mov %eax,errno
+ sbb %eax,%eax # eax = eax - eax - CY = -1
+#endif
+.Lnoerror:
+ pop %ebx
+ pop %esi
+ pop %edi
+ ret
+.size __unified_return,.-__unified_return
+
+#ifdef WANT_SYSENTER
+.Lcallint80:
+ int $0x80
+ ret
+#endif
diff --git a/system/i386/unified_256.S b/system/i386/unified_256.S
new file mode 100644
index 0000000..76436ce
--- /dev/null
+++ b/system/i386/unified_256.S
@@ -0,0 +1,7 @@
+.text
+.global __unified_syscall_256
+.type __unified_syscall_256,@function
+__unified_syscall_256:
+ movzwl %ax,%eax
+ jmp __unified_return
+.size __unified_syscall_256,.-__unified_syscall_256
diff --git a/system/x86_64/Flags b/system/x86_64/Flags
new file mode 100644
index 0000000..af36a53
--- /dev/null
+++ b/system/x86_64/Flags
@@ -0,0 +1 @@
+OPTIMIZATION =
diff --git a/system/x86_64/Makefile b/system/x86_64/Makefile
new file mode 100644
index 0000000..6b11a00
--- /dev/null
+++ b/system/x86_64/Makefile
@@ -0,0 +1,5 @@
+include Flags
+include ../Files
+
+system.a: unified.o $(ALL) __restore_rt.o
+ ar cr $@ $^
diff --git a/system/x86_64/__restore_rt.S b/system/x86_64/__restore_rt.S
new file mode 100644
index 0000000..34a5d3f
--- /dev/null
+++ b/system/x86_64/__restore_rt.S
@@ -0,0 +1,8 @@
+.text
+.align 16
+.global __restore_rt
+.type __restore_rt,@function
+__restore_rt:
+ movq $15, %rax
+ syscall
+ hlt
diff --git a/system/x86_64/gettimeofday.S b/system/x86_64/gettimeofday.S
new file mode 100644
index 0000000..8c2f83b
--- /dev/null
+++ b/system/x86_64/gettimeofday.S
@@ -0,0 +1,18 @@
+.text
+.global gettimeofday
+.type gettimeofday,@function
+gettimeofday:
+ mov $0xffffffffff600000,%rax
+ callq *%rax
+ cmpq $-128, %rax
+ jbe 1f
+ negl %eax
+ pushq %rax
+ call __errno_location
+ popq %rcx
+ movl %ecx,(%rax)
+ orq $-1, %rax
+1:
+ ret
+.Lhere:
+ .size gettimeofday,.Lhere-gettimeofday
diff --git a/system/x86_64/start.S b/system/x86_64/start.S
new file mode 100644
index 0000000..b31b9b1
--- /dev/null
+++ b/system/x86_64/start.S
@@ -0,0 +1,17 @@
+.text
+.global _start
+_start:
+ popq %rdi /* %rdi = argc */
+ movq %rsp,%rsi /* %rsi = argv */
+ pushq %rdi
+
+ leaq 8(%rsi,%rdi,8),%rdx /* %rdx = envp = (8*rdi)+%rsi+8 */
+
+ movq %rdx, environ(%rip)
+ call main
+ movq %rax, %rdi /* return value */
+ call exit
+ hlt
+.Lstart:
+ .size _start,.Lstart-_start
+
diff --git a/system/x86_64/syscalls.h b/system/x86_64/syscalls.h
new file mode 100644
index 0000000..b4f1476
--- /dev/null
+++ b/system/x86_64/syscalls.h
@@ -0,0 +1,20 @@
+#include <asm/unistd.h>
+
+#define syscall_weak(name,wsym,sym) \
+.text; \
+.type wsym,@function; \
+.weak wsym; \
+wsym: ; \
+.type sym,@function; \
+.global sym; \
+sym: \
+ mov $__NR_##name,%al; \
+ jmp __unified_syscall
+
+#define syscall(name,sym) \
+.text; \
+.type sym,@function; \
+.global sym; \
+sym: \
+ mov $__NR_##name,%al; \
+ jmp __unified_syscall
diff --git a/system/x86_64/unified.S b/system/x86_64/unified.S
new file mode 100644
index 0000000..c4dff7d
--- /dev/null
+++ b/system/x86_64/unified.S
@@ -0,0 +1,27 @@
+#define SYS_exit 0x3c
+
+.text
+.weak exit
+exit:
+.global _exit
+_exit:
+ mov $SYS_exit,%al
+
+.global __unified_syscall
+__unified_syscall:
+ movzbl %al, %eax
+ mov %rcx, %r10
+ syscall
+ cmpq $-128, %rax
+ jbe .Lnoerror
+ negl %eax
+ pushq %rax
+ call __errno_location
+ popq %rcx
+ movl %ecx,(%rax)
+ orq $-1, %rax
+.Lnoerror:
+
+ ret
+.Lhere:
+ .size __unified_syscall,.Lhere-__unified_syscall
diff --git a/system/x86_64/waitpid.S b/system/x86_64/waitpid.S
new file mode 100644
index 0000000..2a85491
--- /dev/null
+++ b/system/x86_64/waitpid.S
@@ -0,0 +1,10 @@
+.text
+.type waitpid,@function
+.weak waitpid
+waitpid:
+.type __libc_waitpid,@function
+.global __libc_waitpid
+__libc_waitpid:
+ xor %rcx,%rcx
+ mov $__NR_wait4,%al
+ jmp __unified_syscall
diff --git a/sysvinit.c b/sysvinit.c
new file mode 100644
index 0000000..b9ee198
--- /dev/null
+++ b/sysvinit.c
@@ -0,0 +1,143 @@
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <sys/poll.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <alloca.h>
+#include "ninitfeatures.h"
+#include "initreq.h"
+#define IREQ sizeof(struct init_request)
+
+static char read_buf[IREQ + 4];
+struct init_request *req = (void *)read_buf;
+static int infd,outfd,flag_fork;
+
+#include "open_inout.h"
+#include "uid.h"
+
+void sighandler(int sig) { (void)sig; while (waitpid(-1,0,WNOHANG) > 0); }
+
+static void do_it(char *A) {
+ int r=0;
+ if (flag_fork) { while ((r=fork()) == -1) nano_sleep(1,0); }
+ if (r==0) {
+ open_inout(INITROOT);
+ msg("Starting service: ",A+1);
+ write(infd, A, str_len(A));
+ read(outfd, req, IREQ);
+
+ close(infd); close(outfd);
+ if (flag_fork) _exit(0);
+ }
+}
+
+void print_env() {
+ char *x = req->i.data;
+ msg("request"," for INIT_CMD_SETENV");
+ while (*x) {
+ msg(x);
+ x += str_len(x);
+ x++;
+ }
+}
+
+int main(int argc, char **argv) {
+ struct pollfd pfd;
+ unsigned long ul[2] = {0,0};
+ int fd, timeout = -1;
+ char power[] = "spowerS";
+
+ errmsg_iam("ninit-sysvinit");
+
+ if (argc>1 && !str_diff(argv[1], "powerS")) {
+ char ch;
+ msg("trying to read and remove: ", INIT_POWERSTATUS);
+
+ fd=open(INIT_POWERSTATUS, O_RDONLY);
+ if (fd>=0) { read(fd,&ch,1); close(fd); }
+ unlink(INIT_POWERSTATUS);
+
+ if ('a' <= ch && ch <= 'z') ch -= 32;
+ switch (ch) {
+ case 'L': case 'O': break;
+ default: ch = 'F';
+ }
+
+ power[6] = ch;
+ do_it(power);
+ return 0;
+ }
+
+ fd = open(INIT_FIFO, O_RDWR | O_NONBLOCK);
+ if (fd < 0) {
+ SYS_mknod(INIT_FIFO,S_IFIFO|0600,0);
+ return -1;
+ }
+ fcntl(fd,F_SETFD,FD_CLOEXEC);
+
+ pfd.fd = fd;
+ pfd.events = POLLIN;
+ read_ulongs("sysvinit-timeout", ul, 2);
+
+ if (ul[0] > 0)timeout = 1000 * ul[0];
+ flag_fork = (ul[1] != 0);
+ // msg(" timeout: ",fu(ul[0]), ",fork-mode: ",flag_fork ? "YES" : "NO");
+
+ if (flag_fork) set_sa(SIGCHLD);
+
+ while (1) {
+ int flag_known = 0;
+ switch (poll(&pfd,1,timeout)) {
+ case -1:
+ if (errno == EINTR) sighandler(SIGCHLD); break;
+ return -1; /* poll failed */
+ case 1: {
+ int r = read(fd, req, IREQ);
+ if (r == (int)IREQ) {
+
+#ifdef SYSVINIT_DUMP_INITREQ
+ if ((r=open("/tmp/__##initreq", O_WRONLY | O_APPEND)) >= 0)
+ { write(r,req,IREQ); close(r); }
+#endif
+
+ if (req->magic == INIT_MAGIC) {
+ char level[] = "slevelS";
+ char *A="";
+
+ switch (req->cmd) {
+ case INIT_CMD_SETENV:
+ print_env();
+ flag_known=1;
+ break;
+ case INIT_CMD_RUNLVL:
+ r = req->runlevel;
+ if ('a' <= r && r <= 'z') r -= 32;
+ level[6] = r;
+ A = level;
+ break;
+ case INIT_CMD_POWEROK:
+ A = power;
+ power[6] = 'O';
+ break;
+ case INIT_CMD_POWERFAIL:
+ A = power;
+ power[6] = 'F';
+ break;
+ case INIT_CMD_POWERFAILNOW:
+ A = power;
+ power[6] = 'L';
+ }
+
+ if (*A) { flag_known=1; do_it(A); }
+ } /* INIT_MAGIC */
+ } /* r == sizeof(...) */
+ if (flag_known==0) carp("unknown", " request");
+ }
+ break;
+ default: /* timeout */
+ return 0;
+ } /* poll */
+ }
+}
diff --git a/t_write.h b/t_write.h
new file mode 100644
index 0000000..fde8339
--- /dev/null
+++ b/t_write.h
@@ -0,0 +1,19 @@
+/* timeout write to outfd more than 4096 bytes */
+
+static void t_write(void *buf, int len) {
+#ifndef INIT_TIMEOUT_WRITE
+ write(outfd,buf,len);
+#else
+ time_t deadline = time(0) + 1;
+ while (len > 0) {
+ int w = write(outfd,buf, (len > PIPE_BUF) ? PIPE_BUF : len);
+ if (w==-1) {
+ if (errno == EINTR) continue;
+ if (errno == EAGAIN && time(0) <= deadline) continue;
+ return;
+ }
+ len -= w;
+ buf += w;
+ }
+#endif
+}
diff --git a/tryservice.h b/tryservice.h
new file mode 100644
index 0000000..ac615eb
--- /dev/null
+++ b/tryservice.h
@@ -0,0 +1,16 @@
+
+static int tryservice(char *home, char *service, char *type, char *other,
+ void *buffer) {
+ int r;
+ open_inout(home);
+ errmsg_puts(-1,0);
+ errmsg_puts(-1,type);
+ errmsg_put(-1,service,str_len(service)+1);
+ if (other)
+ errmsg_puts(-1,other);
+ errmsg_puts(infd,0);
+
+ r=read(outfd,buffer, 4*sizeof(struct process));
+ close(infd); close(outfd);
+ return r;
+}
diff --git a/tryservice_nsvc.h b/tryservice_nsvc.h
new file mode 100644
index 0000000..e83feb0
--- /dev/null
+++ b/tryservice_nsvc.h
@@ -0,0 +1,34 @@
+#ifdef CLEAN_SERVICE
+static char *cleanservice(char* service) {
+ char* x;
+ size_t len = str_len(initroot);
+ if (!byte_diff(service,len,initroot) && service[len] == '/')
+ service += len+1;
+ x=service+str_len(service);
+ while (x>service && x[-1]=='/') --x;
+ x[0]=0;
+ return service;
+}
+#endif
+
+
+/* return nonzero if error */
+static int tryservice(char *service, char ch) {
+ char *y=buf, *x=service;
+ int len;
+
+#ifdef CLEAN_SERVICE
+ x = cleanservice(x);
+#endif
+
+ *y++ = ch;
+ y += str_copyn(y,x,240);
+ *y++ = 0;
+ if (nsvc_other) y += str_copyn(y, nsvc_other, 15);
+
+ write(infd,buf,y-buf);
+ len=read(outfd,buf,BUFFER_TMP_LEN);
+
+ if (ch != 's') return (len != sizeof(struct process));
+ return (len!=1 || buf[0]!='1');
+}
diff --git a/uid.h b/uid.h
new file mode 100644
index 0000000..6727291
--- /dev/null
+++ b/uid.h
@@ -0,0 +1,26 @@
+#ifdef INIT_SYSTEM
+#define set_sa(a) system_set_sigaction(a)
+#else
+#define set_sa(a) set_sigaction(a)
+#endif
+
+#if defined(INIT_SYSTEM) && defined(__i386__)
+typedef unsigned short gid__t;
+extern int SYS_mknod(const char *path, gid__t mode, gid__t dev);
+extern int SYS_chown(const char *path, gid__t owner, gid__t group);
+extern int SYS_setgroups(size_t size, const gid__t *list);
+#else
+typedef gid_t gid__t;
+#define SYS_chown chown
+#define SYS_mknod mknod
+#define SYS_setgroups setgroups
+#endif
+
+#if defined(__linux__) && defined(INIT_SYSTEM)
+/* LINUX_REBOOT_MAGIC1 0xfee1dead
+ LINUX_REBOOT_MAGIC2 672274793 */
+extern int SYS_reboot(int magic, int magic2, int cmd);
+#define set_reboot(cmd) SYS_reboot(0xfee1dead, 672274793, cmd)
+#else
+#define set_reboot reboot
+#endif
diff --git a/update.c b/update.c
new file mode 100644
index 0000000..a7dcc24
--- /dev/null
+++ b/update.c
@@ -0,0 +1,96 @@
+#include <unistd.h>
+#include <fcntl.h>
+#include <alloca.h>
+#include "ninitfeatures.h"
+
+static int infd=-1, outfd=-1;
+static uint32t env_free=0;
+#include "contrib/put_env.h"
+
+static void die_xx(char *s) {
+ char *aa[3];
+ INIT_ARGS3(aa, "/sbin/ninit","",0);
+ write(2,"\nupdate: ",9);
+ write(2,s,str_len(s));
+ write(2,"\n",1);
+ while (1) {
+ execve(*aa,aa,environ); /* really panic !!! */
+ nano_sleep(0,800000000);
+ }
+}
+static void safe_read(void *buf, int len) {
+ if (read(infd,buf,len) != len) die_xx("error reading");
+}
+static void safe_write() {
+ if (write(outfd,"1",1) != 1) die_xx("error writing");
+}
+
+int main(int Argc, char **Argv) {
+ uint32t len,i,k;
+ char **argv, **env, *s;
+
+ s = Argv[0];
+ s[str_rchr(s, '/')] = 0;
+ chdir(s);
+
+ infd=open("../in",O_RDWR);
+ outfd=open("../out",O_RDWR|O_NONBLOCK);
+ fcntl(infd,F_SETFD,FD_CLOEXEC);
+ fcntl(outfd,F_SETFD,FD_CLOEXEC);
+
+ if (infd<0 || outfd<0) {
+ write(outfd,"0",1);
+ die_xx("pipes error");
+ }
+ write(outfd,"7",1);
+
+ safe_read(&len,sizeof(len));
+ argv = alloca((len+5) * sizeof(char *));
+ if (argv==0) die_xx("out of memory");
+ safe_write();
+
+ for (i=0; i<len; i++) { /* argv */
+ safe_read(&k, sizeof(k));
+ if (k) {
+ if ((s=alloca(k+1))==0) die_xx("out of memory");
+ safe_read(s,k);
+ s[k] = 0;
+ } else s = "";
+ argv[i] = s;
+ safe_write();
+ }
+ argv[i] = 0;
+
+ /* ----- environ ----- */
+ safe_read(&len,sizeof(len));
+ if (len==0) { safe_write(); goto ready; }
+
+ env_free=len+1;
+ if (environ==0) environ = Argv+Argc;
+ for (k=0; environ[k]; ) k++;
+ env = alloca((len+k+5) * sizeof(char *));
+ if (env==0) die_xx("out of memory");
+
+ byte_copy(env, (k+1)*sizeof(char *), environ);
+ environ=env;
+ safe_write();
+
+ for (i=0; i<len; i++) { /* environ */
+ safe_read(&k, sizeof(k));
+ if (k==0) environ[0] = 0;
+ else {
+ if ((s=alloca(k+1))==0) die_xx("out of memory");
+ safe_read(s,k);
+ s[k] = 0;
+ put_env(s);
+ }
+ safe_write();
+ }
+
+ ready:
+ safe_read(&k,sizeof(k)); /* k = "doit" */
+ chdir("/");
+ execve(*argv, argv, environ);
+ die_xx("PANIC");
+ return 1;
+}
diff --git a/wait.c b/wait.c
new file mode 100644
index 0000000..998bd8d
--- /dev/null
+++ b/wait.c
@@ -0,0 +1,96 @@
+#include <unistd.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/signal.h>
+#include <sys/wait.h>
+#include "ninitfeatures.h"
+
+#define SERVICE Argv[1]
+#define HOME Argv[2]
+#define SYSDIR Argv[3]
+
+static unsigned long env_free, env_len;
+static int infd, outfd;
+
+#include "process_defs.h"
+#include "open_inout.h"
+#include "tryservice.h"
+#include "contrib/put_env.h"
+#include "wait_services.h"
+
+#define NI_TEXT_FILES 4
+enum { NI_environ,NI_softlimit,NI_wait,NI_cron };
+
+int main(int Argc, char **Argv) {
+ char **env, **xx, *s;
+ char *tmp[NI_TEXT_FILES];
+ static char **aa[NI_TEXT_FILES];
+ int i,fd,len;
+
+ errmsg_iam("run-wait");
+ if (Argc<4) {
+ usage:
+ carp("usage: ",errmsg_argv0," service home sys");
+ _exit(1);
+ }
+
+ if (chdir(HOME) || chdir(SERVICE)) goto usage;
+ INIT_ARGS4(tmp, "environ","softlimit","wait","cron");
+
+ for (i=0; i < NI_TEXT_FILES; i++) {
+ int offset=0;
+ if ((fd = open(tmp[i], O_RDONLY)) >=0) {
+ if (i==0) {
+ if (environ==0) environ = Argv+Argc;
+ for (env=environ; *env; ++env) env_len++;
+ offset = env_len + 3;
+ }
+
+ if (GLOBAL_READ(fd,s, len,24000)) _exit(1);
+ close(fd);
+ s[len]=0;
+
+ len = splitmem(0,s,'\n'); if (i==0) env_free=len;
+ xx=alloca((len+offset+1) * sizeof(char*)); if (xx==0) _exit(1);
+ xx += offset;
+ aa[i] = xx;
+ splitmem(xx,s,'\n');
+ skip_comments(xx);
+ }
+ }
+
+ if (aa[NI_environ]) {
+ env = aa[NI_environ] - (env_len + 3);
+ byte_copy(env, (env_len+1)*sizeof(char *), environ);
+ environ = env;
+
+ env = aa[NI_environ];
+ if (**env==0) environ[0]=0;
+ for (; *env; env++) put_env(*env);
+ }
+
+ if (aa[NI_softlimit]) {
+ errmsg_iam("run-wait: softlimit");
+ softlimit(aa[NI_softlimit]);
+ }
+
+ if (aa[NI_wait]) wait_services(aa[NI_wait], HOME);
+
+ if ((xx=aa[NI_cron])) {
+ unsigned long cr[2];
+ struct process pr[6];
+ cron(xx, cr);
+ if (cr[0]) {
+ len = tryservice(HOME,SERVICE,"c",fu(cr[0]), pr);
+ if (cr[1] == 0 && len == sizeof(pr[0]) && pr->pr_finish == 0) return 0;
+ }
+ }
+
+ Argv[0] = "-run";
+ if (!access("pause-wait", R_OK)) kill(getpid(), SIGSTOP);
+ if (chdir(HOME) || chdir(SYSDIR)) return 1;
+
+ execve("./run",Argv,environ);
+ return 1;
+}
diff --git a/wait_services.h b/wait_services.h
new file mode 100644
index 0000000..b99ab79
--- /dev/null
+++ b/wait_services.h
@@ -0,0 +1,37 @@
+static void wait_services(char **argv, char *home) {
+ char *s;
+ struct process pr[6];
+ int r,count;
+ unsigned long ul[2], ug[2] = { RUN_MAXWAIT, 1 };
+
+ read_ulongs("maxwait", ug, 2);
+ if (ug[1]==0) ug[1] = 1;
+
+ for (; *argv; argv++) {
+ s = *argv;
+ if (*s == 0) continue;
+
+ r = str_chr(s,':');
+ if (s[r]) {
+ s[r] = 0;
+ r = scan_ulongs(s+r+1,ul,2, scan_ulong,':',&count);
+ } else r=0;
+
+ if (r<1) ul[0] = ug[0];
+ if (r<2) ul[1] = ug[1];
+ count = ul[0];
+
+ /* we have to ask EACH TIME for PID. pidfile changes it */
+ while (1) {
+ r = tryservice(home,s,"p", 0, pr);
+ if (r == sizeof(pr[0]))
+ if (pr->pid != 1) {
+ nano_sleep(ul[1], 0);
+ if (ul[0]==0) continue;
+ count -= ul[1];
+ if (count > 0) continue;
+ }
+ break;
+ }
+ }
+}