libptytty-2.0/0000775000175000017500000000000014102520160012700 5ustar giaqemgiaqemlibptytty-2.0/Changes0000644000175000017500000000762514102517460014214 0ustar giaqemgiaqem2.0 Wed Aug 4 15:59:08 CEST 2021 - change build system to cmake. - add pkg-config file. - ptytty::login no longer writes a utmp, wtmp and lastlog record when utmpx is available. - ptytty::login now provides the time with microseconds in utmpx, wtmpx and lastlogx records. 1.8 Thu Feb 25 21:17:12 CET 2016 - fix m4 underquoting bug that prevented utmpx detection from working on systems without the utmp.h header, such as freebsd 9 and newer. - fix ptytty::get hang on AIX. - ptytty::get on Solaris no longer makes the tty controlling when the calling process does not have a controlling terminal. Bug introduced in ptytty-1.4. 1.7 Thu Mar 19 22:04:33 CET 2015 - fix various bugs that prevented the lib from working correctly on some systems when using the helper process. - fix compilation on solaris with g++ >= 4.7. - fix compilation on freebsd/openbsd. - nuke the pid check when !PTYTTY_REENTRANT: it breaks urxvtd, and serves little purpose as it is off in normal builds anyway. - add ecb.h. 1.6 Sat Jan 21 16:59:22 CET 2012 - fix compilation on solaris. - remove dependency on STL. 1.5 Thu Dec 22 23:32:08 CET 2011 - INCOMPATIBLE CHANGE: ptytty::get no longer sets the pty fd non-blocking. - the login_shell argument of ptytty::login is now properly honoured. - ptytty::get no longer makes the pty a controlling terminal when using posix_openpt. - remove support for utmpx ut_session extension, it serves no purpose. - remove support for writing lastlog when the lastlog file is a directory, no system is known to require this code. - do not clear ut_user in utmpx logout records because pututxline fails otherwise on solaris. - fix writing of bsd style utmp when the tty is closed before calling ptytty::login. - fix compilation on systems with bsd style utmp and no utmpx, such as openbsd. 1.4 Wed Jun 29 14:36:19 CEST 2011 - push stream modules in ptytty::get rather than in ptytty::make_controlling_tty. This makes tty ioctls (such as TIOCSWINSZ, to set the window size) work even if make_controlling_tty has not been called. - clear user and host fields in utmp(x) logout records. - fix utmpx detection on upcoming FreeBSD 9. - switch to automake. - refactoring and cleanup. 1.3 Sun May 3 23:44:16 CEST 2009 - fedora 9 apparently has isastream but not the stropts.h header file for it, work around this (reported by Tuncer Ayaz). - detect if lastlog is a directory at runtime. - assume ttyslot availability for bsd style utmp. - make sure the alignment for fd passing control messages is correct and work around some NetBSD issues (Taylor R Campbell). - add some missing include statements. 1.2 Thu Aug 2 15:50:02 CEST 2007 - restore the SysV way of acquiring a controlling terminal (for systems having no TIOCSCTTY such as cygwin or solaris). - when using openpty, get the slave name with ttyname. - fix invalid free when using _getpty. - update config.guess and config.sub. 1.1 Mon Feb 12 18:25:07 CET 2007 - when using the helper, close the pty and tty fd also in the main process (patch by Andrei Paskevich). - implement ptytty::sanitise_stdfd to ensure stdin/out/error exist and are usable and use it. 1.0 Tue Oct 3 13:18:00 CEST 2006 - do not call chmod/chown with 0 argument. - reformatted ptytty.C to hopefully be more clear. 0.2 Thu Jul 20 04:57:26 CEST 2006 - move include files outside #ifdef PTYTTY_HELPER in proxy.C. - added proxy.C to cvs (was missing before). 0.1 Wed Jan 25 22:56:36 CET 2006 - added the C API. - small freebsd patch to fdpass.C by Thierry Thomas. - added eg/c-sample.c. - small fixes to the C api. - restructured source files. - added set*uid checks to ptytty.m4. 0.0 - originally cloned from rxvt-unicode. libptytty-2.0/doc/0000755000175000017500000000000014102517460013454 5ustar giaqemgiaqemlibptytty-2.0/doc/libptytty.3.pod0000644000175000017500000002157414102517460016376 0ustar giaqemgiaqem=head1 NAME libptytty - OS independent and secure pty/tty and utmp/wtmp/lastlog handling =head1 SYNOPSIS cc ... -lptytty #include // C++ ptytty *pty = ptytty::create (); if (!pty->get ()) // error allocating pty if (we want utmp) pty->login (process_pid, 0, "remote.host"); else if (we want utmp AND wtmp/lastlog) pty->login (process_pid, 1, "remote.host"); // we are done with it delete pty; // C PTYTTY pty = ptytty_create (); if (!ptytty_get (pty)) // error allocating pty if (we want utmp) ptytty_login (pty, process_pid, 0, "remote.host"); else if (we want utmp AND wtmp/lastlog) ptytty_login (pty, process_pid, 1, "remote.host"); // we are done with it ptytty_delete (pty); See also the F directory, which currently contains the F file that spawns a login shell from C using libptytty. =head1 DESCRIPTION Libptytty is a small library that offers pseudo-tty management in an OS-independent way. It was created out of frustration over the many differences of pty/tty handling in different operating systems for the use inside C. In addition to offering mere pty/tty management, it also offers session database support (utmp and optional wtmp/lastlog updates for login shells). It also supports fork'ing after startup and dropping privileges in the calling process, so in case the calling process gets compromised by the user starting the program there is less to gain, as only the helper process runs with privileges (e.g. setuid/setgid), which reduces the area of attack immensely. Libptytty is written in C++, but it also offers a C-only API. =head1 INSTALLATION libptytty uses C as build system. To build libptytty, install C and run the following commands from either the libptytty source directory or a separate build directory: cmake -DCMAKE_INSTALL_PREFIX= -DBUILD_SHARED_LIBS=ON cmake --build . cmake --install . =head1 SECURITY CONSIDERATIONS I<< B >> If you write a typical terminal-like program that just wants one or more ptys, you should call the C method (C: C function) as the very first thing in your program: int main (int argc, char *argv[]) { // do nothing here ptytty::init (); // in C: ptytty_init (); // initialise, parse arguments, etc. } This checks whether the program runs setuid or setgid. If yes then it will fork a helper process and drop privileges. Some programs need finer control over if and when this helper process is started, and if and how to drop privileges. For those programs, the methods C and C (and possibly C) are more useful. =head1 C++ INTERFACE: THE ptytty CLASS =head2 STATIC METHODS =over =item ptytty::init () The default way to initialise libptytty. Must be called immediately as the first thing in the C
function, or earlier e.g. during static construction time. The earlier, the better. This method calls C and then checks whether the program runs with setuid/setgid permissions and, if yes, spawns a helper process for pty/tty management. It then drops the privileges completely, so the actual program runs without setuid/setgid privileges. On failure, this method throws a C exception. =item ptytty::use_helper () Tries to start a helper process that retains privileges even when the calling process does not. This is usually called from C when it detects that the program is running setuid or setgid, but can be called manually if it is inconvenient to drop privileges at startup, or when you are not running setuid/setgid but want to drop privileges (e.g. when running as a root-started daemon). This method will try not to start more than one helper process. The same helper process can usually be used both from the process starting it and all its fork'ed (not exec'ed) children. On failure, this method throws a C exception. =item ptytty::drop_privileges () Drops privileges completely, i.e. sets real, effective and saved user id to the real user id. Useful to make sure that the process doesn't run with special privileges. On failure, this method throws a C exception. =item ptytty::sanitise_stdfd () Checks whether file descriptors 0, 1 and 2 (stdin, stdout and stderr) are valid (open) and, if not, connects them to F or F if possible. This is necessary because libptytty might want to output error messages to those descriptors, which at the time of outputting the error message, might be connected to something unsuitable opened by the unsuspecting program itself (this can be a security issue). On failure, this method throws a C exception. =item bool success = ptytty::send_fd (int socket, int fd) Utility method to send a file descriptor over a unix domain socket. Returns true if successful, false otherwise. This method is only exposed for your convenience and is not required for normal operation. =item int fd = ptytty::recv_fd (int socket) Utility method to receive a file descriptor over a unix domain socket. Returns the fd if successful and C<-1> otherwise. This method is only exposed for your convenience and is not required for normal operation. =item ptytty *pty = ptytty::create () Creates new ptytty object. Creation does not yet do anything besides allocating the structure. A static method is used because the actual ptytty implementation can differ at runtime, so you need a dynamic object creation facility. =back =head2 DYNAMIC/SESSION-RELATED DATA MEMBERS AND METHODS =over =item int pty_fd = pty->pty =item int tty_fd = pty->tty These members contain the pty and tty file descriptors, respectively. They initially contain C<-1> until a successful call to C. =item bool success = pty->get () Tries to find, allocate and initialise a new pty/tty pair. Returns C when successful. If the helper process is running and there is a protocol error, this method throws a C exception. =item pty->login (int cmd_pid, bool login_shell, const char *hostname) Creates an entry in the systems session database(s) (utmp, wtmp, lastlog). C must be the pid of the process representing the session (such as the login shell), C defines whether the session is associated with a login, which influences whether wtmp and lastlog entries are created, and C should identify the "hostname" the user logs in from, which often is the value of the C variable or tty line in case of local logins. Calling this method is optional. A session starts at the time of the login call and extends until the ptytty object is destroyed. =item pty->close_tty () Closes the tty. Useful after forking in the parent/pty process. =item bool success = pty->make_controlling_tty () Tries to make the pty/tty pair the controlling terminal of the current process. Useful after forking in the child/tty process. =item pty->set_utf8_mode (bool on) On systems supporting special UTF-8 line disciplines (e.g. Linux), this tries to enable this discipline for the given pty. Can be called at any time to change the mode. =back =head1 C INTERFACE: THE ptytty FAMILY OF FUNCTIONS =over =item ptytty_init () See C. =item PTYTTY ptytty_create () Creates a new opaque PTYTTY object and returns it. Do not try to access it in any way except by testing it for truthness (e.g. C). See C. =item int ptytty_pty (PTYTTY ptytty) Return the pty file descriptor. See C<< pty->pty >>. =item int ptytty_tty (PTYTTY ptytty) Return the tty file descriptor. See C<< pty->tty >>. =item void ptytty_delete (PTYTTY ptytty) Destroys the PTYTTY object, freeing the pty/tty pair and cleaning up the utmp/wtmp/lastlog databases, if initialised/used. Same as C in C++. =item int ptytty_get (PTYTTY ptytty) See C<< pty->get >>, returns 0 in case of an error, non-zero otherwise. =item void ptytty_login (PTYTTY ptytty, int cmd_pid, bool login_shell, const char *hostname) See C<< pty->login >>. =item void ptytty_close_tty (PTYTTY ptytty) See C<< pty->close_tty >>. =item int ptytty_make_controlling_tty (PTYTTY ptytty) See C<< pty->make_controlling_tty >>. =item void ptytty_set_utf8_mode (PTYTTY ptytty, int on) See C<< pty->set_utf8_mode >>. =item void ptytty_drop_privileges () See C<< ptytty::drop_privileges >>. =item void ptytty_use_helper () See C<< ptytty::use_helper >>. =back =head1 PORTABILITY To date, libptytty has been tested on the following platforms: =over =item GNU/Linux =item FreeBSD =item NetBSD =item OpenBSD =item macOS =item Solaris =item AIX =back =head1 BUGS You kiddin'? =head1 AUTHORS Emanuele Giaquinta , Marc Alexander Lehmann . libptytty-2.0/doc/libptytty.30000644000175000017500000003626414102517460015617 0ustar giaqemgiaqem.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "libptytty 3" .TH libptytty 3 "2021-07-27" "2.0" "LIBPTYTTY" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" libptytty \- OS independent and secure pty/tty and utmp/wtmp/lastlog handling .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 1 \& cc ... \-lptytty \& \& #include \& \& \& // C++ \& ptytty *pty = ptytty::create (); \& \& if (!pty\->get ()) \& // error allocating pty \& \& if (we want utmp) \& pty\->login (process_pid, 0, "remote.host"); \& else if (we want utmp AND wtmp/lastlog) \& pty\->login (process_pid, 1, "remote.host"); \& \& // we are done with it \& delete pty; \& \& \& // C \& PTYTTY pty = ptytty_create (); \& \& if (!ptytty_get (pty)) \& // error allocating pty \& \& if (we want utmp) \& ptytty_login (pty, process_pid, 0, "remote.host"); \& else if (we want utmp AND wtmp/lastlog) \& ptytty_login (pty, process_pid, 1, "remote.host"); \& \& // we are done with it \& ptytty_delete (pty); .Ve .PP See also the \fIeg/\fR directory, which currently contains the \fIc\-sample.c\fR file that spawns a login shell from C using libptytty. .SH "DESCRIPTION" .IX Header "DESCRIPTION" Libptytty is a small library that offers pseudo-tty management in an OS-independent way. It was created out of frustration over the many differences of pty/tty handling in different operating systems for the use inside \f(CW\*(C`rxvt\-unicode\*(C'\fR. .PP In addition to offering mere pty/tty management, it also offers session database support (utmp and optional wtmp/lastlog updates for login shells). .PP It also supports fork'ing after startup and dropping privileges in the calling process, so in case the calling process gets compromised by the user starting the program there is less to gain, as only the helper process runs with privileges (e.g. setuid/setgid), which reduces the area of attack immensely. .PP Libptytty is written in \*(C+, but it also offers a C\-only \s-1API.\s0 .SH "INSTALLATION" .IX Header "INSTALLATION" libptytty uses \f(CW\*(C`CMake\*(C'\fR as build system. To build libptytty, install \&\f(CW\*(C`CMake\*(C'\fR and run the following commands from either the libptytty source directory or a separate build directory: .PP .Vb 3 \& cmake \-DCMAKE_INSTALL_PREFIX= \-DBUILD_SHARED_LIBS=ON \& cmake \-\-build . \& cmake \-\-install . .Ve .SH "SECURITY CONSIDERATIONS" .IX Header "SECURITY CONSIDERATIONS" \&\fI\f(BIIt is of paramount importance that you at least read the following paragraph!\fI\fR .PP If you write a typical terminal-like program that just wants one or more ptys, you should call the \f(CW\*(C`ptytty::init ()\*(C'\fR method (C: \f(CW\*(C`ptytty_init ()\*(C'\fR function) as the very first thing in your program: .PP .Vb 5 \& int main (int argc, char *argv[]) \& { \& // do nothing here \& ptytty::init (); \& // in C: ptytty_init (); \& \& // initialise, parse arguments, etc. \& } .Ve .PP This checks whether the program runs setuid or setgid. If yes then it will fork a helper process and drop privileges. .PP Some programs need finer control over if and when this helper process is started, and if and how to drop privileges. For those programs, the methods \f(CW\*(C`ptytty::use_helper\*(C'\fR and \f(CW\*(C`ptytty::drop_privileges\*(C'\fR (and possibly \&\f(CW\*(C`ptytty::sanitise_stdfd\*(C'\fR) are more useful. .SH "\*(C+ INTERFACE: THE ptytty CLASS" .IX Header " INTERFACE: THE ptytty CLASS" .SS "\s-1STATIC METHODS\s0" .IX Subsection "STATIC METHODS" .IP "ptytty::init ()" 4 .IX Item "ptytty::init ()" The default way to initialise libptytty. Must be called immediately as the first thing in the \f(CW\*(C`main\*(C'\fR function, or earlier e.g. during static construction time. The earlier, the better. .Sp This method calls \f(CW\*(C`sanitise_stdfd\*(C'\fR and then checks whether the program runs with setuid/setgid permissions and, if yes, spawns a helper process for pty/tty management. It then drops the privileges completely, so the actual program runs without setuid/setgid privileges. .Sp On failure, this method throws a \f(CW\*(C`ptytty_error\*(C'\fR exception. .IP "ptytty::use_helper ()" 4 .IX Item "ptytty::use_helper ()" Tries to start a helper process that retains privileges even when the calling process does not. This is usually called from \f(CW\*(C`ptytty::init\*(C'\fR when it detects that the program is running setuid or setgid, but can be called manually if it is inconvenient to drop privileges at startup, or when you are not running setuid/setgid but want to drop privileges (e.g. when running as a root-started daemon). .Sp This method will try not to start more than one helper process. The same helper process can usually be used both from the process starting it and all its fork'ed (not exec'ed) children. .Sp On failure, this method throws a \f(CW\*(C`ptytty_error\*(C'\fR exception. .IP "ptytty::drop_privileges ()" 4 .IX Item "ptytty::drop_privileges ()" Drops privileges completely, i.e. sets real, effective and saved user id to the real user id. Useful to make sure that the process doesn't run with special privileges. .Sp On failure, this method throws a \f(CW\*(C`ptytty_error\*(C'\fR exception. .IP "ptytty::sanitise_stdfd ()" 4 .IX Item "ptytty::sanitise_stdfd ()" Checks whether file descriptors 0, 1 and 2 (stdin, stdout and stderr) are valid (open) and, if not, connects them to \fI/dev/tty\fR or \&\fI/dev/null\fR if possible. This is necessary because libptytty might want to output error messages to those descriptors, which at the time of outputting the error message, might be connected to something unsuitable opened by the unsuspecting program itself (this can be a security issue). .Sp On failure, this method throws a \f(CW\*(C`ptytty_error\*(C'\fR exception. .IP "bool success = ptytty::send_fd (int socket, int fd)" 4 .IX Item "bool success = ptytty::send_fd (int socket, int fd)" Utility method to send a file descriptor over a unix domain socket. Returns true if successful, false otherwise. This method is only exposed for your convenience and is not required for normal operation. .IP "int fd = ptytty::recv_fd (int socket)" 4 .IX Item "int fd = ptytty::recv_fd (int socket)" Utility method to receive a file descriptor over a unix domain socket. Returns the fd if successful and \f(CW\*(C`\-1\*(C'\fR otherwise. This method is only exposed for your convenience and is not required for normal operation. .IP "ptytty *pty = ptytty::create ()" 4 .IX Item "ptytty *pty = ptytty::create ()" Creates new ptytty object. Creation does not yet do anything besides allocating the structure. .Sp A static method is used because the actual ptytty implementation can differ at runtime, so you need a dynamic object creation facility. .SS "\s-1DYNAMIC/SESSION\-RELATED DATA MEMBERS AND METHODS\s0" .IX Subsection "DYNAMIC/SESSION-RELATED DATA MEMBERS AND METHODS" .IP "int pty_fd = pty\->pty" 4 .IX Item "int pty_fd = pty->pty" .PD 0 .IP "int tty_fd = pty\->tty" 4 .IX Item "int tty_fd = pty->tty" .PD These members contain the pty and tty file descriptors, respectively. They initially contain \f(CW\*(C`\-1\*(C'\fR until a successful call to \f(CW\*(C`ptytty::get\*(C'\fR. .IP "bool success = pty\->get ()" 4 .IX Item "bool success = pty->get ()" Tries to find, allocate and initialise a new pty/tty pair. Returns \f(CW\*(C`true\*(C'\fR when successful. .Sp If the helper process is running and there is a protocol error, this method throws a \f(CW\*(C`ptytty_error\*(C'\fR exception. .IP "pty\->login (int cmd_pid, bool login_shell, const char *hostname)" 4 .IX Item "pty->login (int cmd_pid, bool login_shell, const char *hostname)" Creates an entry in the systems session database(s) (utmp, wtmp, lastlog). \&\f(CW\*(C`cmd_pid\*(C'\fR must be the pid of the process representing the session (such as the login shell), \f(CW\*(C`login_shell\*(C'\fR defines whether the session is associated with a login, which influences whether wtmp and lastlog entries are created, and \f(CW\*(C`hostname\*(C'\fR should identify the \*(L"hostname\*(R" the user logs in from, which often is the value of the \f(CW\*(C`DISPLAY\*(C'\fR variable or tty line in case of local logins. .Sp Calling this method is optional. A session starts at the time of the login call and extends until the ptytty object is destroyed. .IP "pty\->close_tty ()" 4 .IX Item "pty->close_tty ()" Closes the tty. Useful after forking in the parent/pty process. .IP "bool success = pty\->make_controlling_tty ()" 4 .IX Item "bool success = pty->make_controlling_tty ()" Tries to make the pty/tty pair the controlling terminal of the current process. Useful after forking in the child/tty process. .IP "pty\->set_utf8_mode (bool on)" 4 .IX Item "pty->set_utf8_mode (bool on)" On systems supporting special \s-1UTF\-8\s0 line disciplines (e.g. Linux), this tries to enable this discipline for the given pty. Can be called at any time to change the mode. .SH "C INTERFACE: THE ptytty FAMILY OF FUNCTIONS" .IX Header "C INTERFACE: THE ptytty FAMILY OF FUNCTIONS" .IP "ptytty_init ()" 4 .IX Item "ptytty_init ()" See \f(CW\*(C`ptytty::init ()\*(C'\fR. .IP "\s-1PTYTTY\s0 ptytty_create ()" 4 .IX Item "PTYTTY ptytty_create ()" Creates a new opaque \s-1PTYTTY\s0 object and returns it. Do not try to access it in any way except by testing it for truthness (e.g. \f(CW\*(C`if (pty) ....\*(C'\fR). See \&\f(CW\*(C`ptytty::create ()\*(C'\fR. .IP "int ptytty_pty (\s-1PTYTTY\s0 ptytty)" 4 .IX Item "int ptytty_pty (PTYTTY ptytty)" Return the pty file descriptor. See \f(CW\*(C`pty\->pty\*(C'\fR. .IP "int ptytty_tty (\s-1PTYTTY\s0 ptytty)" 4 .IX Item "int ptytty_tty (PTYTTY ptytty)" Return the tty file descriptor. See \f(CW\*(C`pty\->tty\*(C'\fR. .IP "void ptytty_delete (\s-1PTYTTY\s0 ptytty)" 4 .IX Item "void ptytty_delete (PTYTTY ptytty)" Destroys the \s-1PTYTTY\s0 object, freeing the pty/tty pair and cleaning up the utmp/wtmp/lastlog databases, if initialised/used. Same as \f(CW\*(C`delete pty\*(C'\fR in \&\*(C+. .IP "int ptytty_get (\s-1PTYTTY\s0 ptytty)" 4 .IX Item "int ptytty_get (PTYTTY ptytty)" See \f(CW\*(C`pty\->get\*(C'\fR, returns 0 in case of an error, non-zero otherwise. .IP "void ptytty_login (\s-1PTYTTY\s0 ptytty, int cmd_pid, bool login_shell, const char *hostname)" 4 .IX Item "void ptytty_login (PTYTTY ptytty, int cmd_pid, bool login_shell, const char *hostname)" See \f(CW\*(C`pty\->login\*(C'\fR. .IP "void ptytty_close_tty (\s-1PTYTTY\s0 ptytty)" 4 .IX Item "void ptytty_close_tty (PTYTTY ptytty)" See \f(CW\*(C`pty\->close_tty\*(C'\fR. .IP "int ptytty_make_controlling_tty (\s-1PTYTTY\s0 ptytty)" 4 .IX Item "int ptytty_make_controlling_tty (PTYTTY ptytty)" See \f(CW\*(C`pty\->make_controlling_tty\*(C'\fR. .IP "void ptytty_set_utf8_mode (\s-1PTYTTY\s0 ptytty, int on)" 4 .IX Item "void ptytty_set_utf8_mode (PTYTTY ptytty, int on)" See \f(CW\*(C`pty\->set_utf8_mode\*(C'\fR. .IP "void ptytty_drop_privileges ()" 4 .IX Item "void ptytty_drop_privileges ()" See \f(CW\*(C`ptytty::drop_privileges\*(C'\fR. .IP "void ptytty_use_helper ()" 4 .IX Item "void ptytty_use_helper ()" See \f(CW\*(C`ptytty::use_helper\*(C'\fR. .SH "PORTABILITY" .IX Header "PORTABILITY" To date, libptytty has been tested on the following platforms: .IP "GNU/Linux" 4 .IX Item "GNU/Linux" .PD 0 .IP "FreeBSD" 4 .IX Item "FreeBSD" .IP "NetBSD" 4 .IX Item "NetBSD" .IP "OpenBSD" 4 .IX Item "OpenBSD" .IP "macOS" 4 .IX Item "macOS" .IP "Solaris" 4 .IX Item "Solaris" .IP "\s-1AIX\s0" 4 .IX Item "AIX" .PD .SH "BUGS" .IX Header "BUGS" You kiddin'? .SH "AUTHORS" .IX Header "AUTHORS" Emanuele Giaquinta , Marc Alexander Lehmann . libptytty-2.0/config.h.cmake0000644000175000017500000000217314102517460015407 0ustar giaqemgiaqem#cmakedefine01 UTMP_SUPPORT #cmakedefine WTMP_SUPPORT #cmakedefine LASTLOG_SUPPORT #cmakedefine HAVE_PTY_H #cmakedefine HAVE_UTIL_H #cmakedefine HAVE_LIBUTIL_H #cmakedefine HAVE_SYS_IOCTL_H #cmakedefine HAVE_STROPTS_H #cmakedefine HAVE_REVOKE #cmakedefine HAVE__GETPTY #cmakedefine HAVE_GETPT #cmakedefine HAVE_POSIX_OPENPT #cmakedefine HAVE_ISASTREAM #cmakedefine01 HAVE_SETUID #cmakedefine01 HAVE_SETREUID #cmakedefine01 HAVE_SETRESUID #cmakedefine UNIX98_PTY #cmakedefine HAVE_OPENPTY #cmakedefine01 __EXTENSIONS__ #cmakedefine _XOPEN_SOURCE ${_XOPEN_SOURCE} #cmakedefine HAVE_STRUCT_UTMP #cmakedefine HAVE_UTMP_HOST #cmakedefine HAVE_UTMP_PID #cmakedefine HAVE_UPDWTMP #cmakedefine HAVE_LASTLOG_H #cmakedefine HAVE_STRUCT_LASTLOG #cmakedefine PT_UTMP_FILE ${PT_UTMP_FILE} #cmakedefine PT_WTMP_FILE ${PT_WTMP_FILE} #cmakedefine PT_LASTLOG_FILE ${PT_LASTLOG_FILE} #cmakedefine HAVE_STRUCT_UTMPX #cmakedefine HAVE_UTMPX_HOST #cmakedefine HAVE_UPDWTMPX #cmakedefine HAVE_UPDLASTLOGX #cmakedefine HAVE_STRUCT_LASTLOGX #cmakedefine PT_WTMPX_FILE ${PT_WTMPX_FILE} #cmakedefine PT_LASTLOGX_FILE ${PT_LASTLOGX_FILE} #cmakedefine TTY_GID_SUPPORT libptytty-2.0/eg/0000755000175000017500000000000013700105747013306 5ustar giaqemgiaqemlibptytty-2.0/eg/c-sample.c0000644000175000017500000000245713700105747015163 0ustar giaqemgiaqem// on my system, this program outputs: // // who; echo 'Hello, World!'; exit // # root pts/9 Jan 25 12:12 (libptytty.example.net) // Hello, World! // #include #include #include #include #include int main (void) { ptytty_init (); PTYTTY pty = ptytty_create (); if (!ptytty_get (pty)) printf ("unable to open pty\n"), exit (EXIT_FAILURE); pid_t pid = fork (); if (pid < 0) printf ("fork error\n"), exit (EXIT_FAILURE); int pty_fd = ptytty_pty (pty); int tty_fd = ptytty_tty (pty); if (pid) { ptytty_close_tty (pty); ptytty_login (pty, pid, 1, "libptytty.example.net"); char s[] = "who; echo 'Hello, World!'; exit\015"; write (pty_fd, s, sizeof (s) - 1); char buf[1024]; for (;;) { int len = read (pty_fd, buf, 1024); if (len <= 0) break; write (STDOUT_FILENO, buf, len); } ptytty_delete (pty); } else { ptytty_make_controlling_tty (pty); close (pty_fd); dup2 (tty_fd, STDIN_FILENO ); dup2 (tty_fd, STDOUT_FILENO); dup2 (tty_fd, STDERR_FILENO); close (tty_fd); execl ("/bin/sh", "-sh", (char *)0); _exit (EXIT_FAILURE); } return EXIT_SUCCESS; } libptytty-2.0/CMakeLists.txt0000644000175000017500000002305314102517460015452 0ustar giaqemgiaqemcmake_minimum_required(VERSION 3.6) project(libptytty VERSION 2.0) set(CMAKE_CXX_STANDARD 11) set(CMAKE_EXPORT_COMPILE_COMMANDS YES) include(CheckCSourceCompiles) include(CheckCSourceRuns) include(CheckFunctionExists) include(CheckIncludeFiles) include(CheckLibraryExists) include(CheckStructHasMember) include(CheckTypeSize) include(GNUInstallDirs) set(VERSION ${PROJECT_VERSION}) set(prefix ${CMAKE_INSTALL_PREFIX}) set(includedir ${CMAKE_INSTALL_FULL_INCLUDEDIR}) set(libdir ${CMAKE_INSTALL_FULL_LIBDIR}) option(BUILD_SHARED_LIBS "build a shared library" ON) option(UTMP_SUPPORT "enable utmp support" ON) option(WTMP_SUPPORT "enable wtmp support (requires UTMP_SUPPORT)" ON) option(LASTLOG_SUPPORT "enable lastlog support (requires UTMP_SUPPORT)" ON) function(PT_FIND_FILE name id) if(DEFINED ${id}) return() elseif(CMAKE_CROSSCOMPILING) message(STATUS "Checking for a fallback location of ${name} - define ${id} in config.h manually") set(${id} "" CACHE STRING "") else() foreach(arg ${ARGN}) if(EXISTS ${arg}) message(STATUS "Checking for a fallback location of ${name} - ${arg}") set(${id} ${arg} CACHE STRING "") return() endif() endforeach() message(STATUS "Checking for a fallback location of ${name} - not found") set(${id} "" CACHE STRING "") endif() endfunction() if(${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU") file(WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/test.cxx" " struct e { }; void f () { try { new e (); } catch (...) { throw; } } ") execute_process( WORKING_DIRECTORY "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp" COMMAND ${CMAKE_C_COMPILER} -x c++ -shared -fPIC -o test.so test.cxx -lsupc++ OUTPUT_VARIABLE OUTPUT ERROR_VARIABLE OUTPUT RESULT_VARIABLE RESULT) if(RESULT EQUAL 0) message(STATUS "Checking for working libsupc++ - yes") set(HAVE_LIBSUPCXX 1 CACHE BOOL "") file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log "libsupc++ test succeeded with the following output:\n" "${OUTPUT}\n") else() message(STATUS "Checking for working libsupc++ - no") set(HAVE_LIBSUPCXX 0 CACHE BOOL "") file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log "libsupc++ test failed with the following output:\n" "${OUTPUT}\n") endif() endif() check_include_files(pty.h HAVE_PTY_H) check_include_files(util.h HAVE_UTIL_H) check_include_files(libutil.h HAVE_LIBUTIL_H) check_include_files(sys/ioctl.h HAVE_SYS_IOCTL_H) check_include_files(stropts.h HAVE_STROPTS_H) check_function_exists(revoke HAVE_REVOKE) check_function_exists(_getpty HAVE__GETPTY) check_function_exists(getpt HAVE_GETPT) check_function_exists(posix_openpt HAVE_POSIX_OPENPT) check_function_exists(isastream HAVE_ISASTREAM) check_function_exists(setuid HAVE_SETUID) check_function_exists(setreuid HAVE_SETREUID) check_function_exists(setresuid HAVE_SETRESUID) check_c_source_compiles( "#include int main () { grantpt (0); unlockpt (0); ptsname (0); } " UNIX98_PTY) if(NOT UNIX98_PTY) check_function_exists(openpty HAVE_OPENPTY) if(NOT HAVE_OPENPTY) find_library(LIBUTIL_PATH util) check_library_exists(util openpty ${LIBUTIL_PATH} HAVE_LIBUTIL) if(HAVE_LIBUTIL) set(HAVE_OPENPTY 1) endif() endif() endif() if(CMAKE_HOST_SOLARIS) # Enable declarations in utmp.h on Solaris when the XPG4v2 namespace is active set(__EXTENSIONS__ 1) endif() check_include_files("sys/types.h;utmp.h" HAVE_UTMP_H) if(HAVE_UTMP_H) set(CMAKE_EXTRA_INCLUDE_FILES "sys/types.h;utmp.h") check_type_size("struct utmp" STRUCT_UTMP) if (HAVE_STRUCT_UTMP) check_struct_has_member( "struct utmp" ut_host "sys/types.h;utmp.h" HAVE_UTMP_HOST) check_struct_has_member( "struct utmp" ut_pid "sys/types.h;utmp.h" HAVE_UTMP_PID) check_function_exists(updwtmp HAVE_UPDWTMP) check_include_files("sys/types.h;lastlog.h" HAVE_LASTLOG_H) if (HAVE_LASTLOG_H) set(CMAKE_EXTRA_INCLUDE_FILES "sys/types.h;lastlog.h") endif() check_type_size("struct lastlog" STRUCT_LASTLOG) endif() set(CMAKE_EXTRA_INCLUDE_FILES) PT_FIND_FILE( utmp PT_UTMP_FILE "/var/run/utmp" "/var/adm/utmp" "/etc/utmp" "/usr/etc/utmp" "/usr/adm/utmp") PT_FIND_FILE( wtmp PT_WTMP_FILE "/var/log/wtmp" "/var/adm/wtmp" "/etc/wtmp" "/usr/etc/wtmp" "/usr/adm/wtmp") PT_FIND_FILE( lastlog PT_LASTLOG_FILE "/var/log/lastlog" "/var/adm/lastlog") endif() check_include_files(utmpx.h HAVE_UTMPX_H) if(HAVE_UTMPX_H) set(CMAKE_EXTRA_INCLUDE_FILES "utmpx.h") check_type_size("struct utmpx" STRUCT_UTMPX) if (HAVE_STRUCT_UTMPX) check_struct_has_member( "struct utmpx" ut_host "sys/types.h;utmpx.h" HAVE_UTMPX_HOST) check_function_exists(updwtmpx HAVE_UPDWTMPX) check_function_exists(updlastlogx HAVE_UPDLASTLOGX) check_type_size("struct lastlogx" STRUCT_LASTLOGX) endif() set(CMAKE_EXTRA_INCLUDE_FILES) PT_FIND_FILE( wtmpx PT_WTMPX_FILE "/var/log/wtmpx" "/var/adm/wtmpx") PT_FIND_FILE( lastlogx PT_LASTLOGX_FILE "/var/log/lastlogx" "/var/adm/lastlogx") endif() if(CMAKE_HOST_SOLARIS) # Enable declarations of msg_control and msg_controllen on Solaris set(CMAKE_REQUIRED_QUIET ON) check_c_source_compiles( " int main () { #if __STDC_VERSION__ < 199901L error #endif } " C99) set(CMAKE_REQUIRED_QUIET OFF) if(C99) set(_XOPEN_SOURCE 600) else() set(_XOPEN_SOURCE 500) endif() set(CMAKE_REQUIRED_DEFINITIONS -D_XOPEN_SOURCE=${_XOPEN_SOURCE}) find_library(LIBSOCKET_PATH socket) check_library_exists(socket sendmsg ${LIBSOCKET_PATH} HAVE_LIBSOCKET) if(HAVE_LIBSOCKET) set(CMAKE_REQUIRED_LIBRARIES socket) endif() endif() # Check for unix-compliant filehandle passing ability check_c_source_compiles( " #include // broken bsds (is that redundant?) need this #include #include #include int main () { struct msghdr msg; struct iovec iov; char buf [100]; char data = 0; iov.iov_base = &data; iov.iov_len = 1; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = buf; msg.msg_controllen = sizeof buf; struct cmsghdr *cmsg = CMSG_FIRSTHDR (&msg); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; cmsg->cmsg_len = 100; *(int *)CMSG_DATA (cmsg) = 5; return sendmsg (3, &msg, 0); } " HAVE_UNIX_FDPASS) if(NOT HAVE_UNIX_FDPASS) message(FATAL_ERROR "libptytty requires unix-compliant filehandle passing ability") endif() check_c_source_runs( " #include #include #include #include int main () { struct stat st; struct group *gr = getgrnam (\"tty\"); char *tty = ttyname (0); return !(gr && tty && stat (tty, &st) == 0 && st.st_gid == gr->gr_gid); } " TTY_GID_SUPPORT) configure_file( config.h.cmake config.h) add_library(ptytty src/c-api.C src/fdpass.C src/logging.C src/proxy.C src/ptytty.C) target_include_directories(ptytty PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) set_target_properties(ptytty PROPERTIES VERSION 0) if(HAVE_LIBSUPCXX) set_target_properties(ptytty PROPERTIES LINKER_LANGUAGE C) target_link_libraries(ptytty PRIVATE supc++) set(CMAKE_CXX_IMPLICIT_LINK_LIBRARIES) endif() if(HAVE_LIBSOCKET) target_link_libraries(ptytty PRIVATE socket) list(APPEND LIBS -lsocket) endif() if(HAVE_LIBUTIL) target_link_libraries(ptytty PRIVATE util) list(APPEND LIBS -lutil) endif() configure_file( libptytty.pc.in libptytty.pc) add_executable(c-sample eg/c-sample.c) target_include_directories(c-sample PRIVATE src) target_link_libraries(c-sample ptytty) add_custom_command( OUTPUT ${CMAKE_SOURCE_DIR}/doc/libptytty.3 DEPENDS ${CMAKE_SOURCE_DIR}/doc/libptytty.3.pod COMMAND pod2man -n libptytty -r ${PROJECT_VERSION} -q\" -c LIBPTYTTY -s3 < ${CMAKE_SOURCE_DIR}/doc/libptytty.3.pod > ${CMAKE_SOURCE_DIR}/doc/libptytty.3 VERBATIM) add_custom_command( OUTPUT ${CMAKE_SOURCE_DIR}/README DEPENDS ${CMAKE_SOURCE_DIR}/doc/libptytty.3.pod COMMAND pod2text < ${CMAKE_SOURCE_DIR}/doc/libptytty.3.pod > ${CMAKE_SOURCE_DIR}/README VERBATIM) add_custom_target(alldoc DEPENDS ${CMAKE_SOURCE_DIR}/doc/libptytty.3 ${CMAKE_SOURCE_DIR}/README) install( TARGETS ptytty DESTINATION ${CMAKE_INSTALL_LIBDIR}) install( FILES src/libptytty.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) install( FILES doc/libptytty.3 DESTINATION ${CMAKE_INSTALL_MANDIR}/man3) install( FILES ${CMAKE_BINARY_DIR}/libptytty.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) set(DISTFILES CMakeLists.txt COPYING Changes README config.h.cmake doc/libptytty.3 doc/libptytty.3.pod eg/c-sample.c libptytty.pc.in src/c-api.C src/ecb.h src/estl.h src/fdpass.C src/libptytty.h src/logging.C src/proxy.C src/ptytty.C src/ptytty.h src/ptytty_conf.h) set(DISTDIR ${PROJECT_NAME}-${PROJECT_VERSION}) add_custom_target(distdir DEPENDS alldoc WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} COMMAND rm -rf ${CMAKE_BINARY_DIR}/${DISTDIR} COMMAND rsync -aRv ${DISTFILES} ${CMAKE_BINARY_DIR}/${DISTDIR}) add_custom_target(dist COMMAND tar cvf - ${DISTDIR} | gzip -vf9 > ${DISTDIR}.tar.gz) add_dependencies(dist distdir) libptytty-2.0/src/0000755000175000017500000000000014102517460013476 5ustar giaqemgiaqemlibptytty-2.0/src/logging.C0000644000175000017500000002117114102517460015232 0ustar giaqemgiaqem/*----------------------------------------------------------------------* * File: logging.C *----------------------------------------------------------------------* * * All portions of code are copyright by their respective author/s. * Copyright (c) 1992 John Bovey * - original version * Copyright (c) 1993 lipka * Copyright (c) 1993 Brian Stempien * Copyright (c) 1995 Raul Garcia Garcia * Copyright (c) 1995 Piet W. Plomp * Copyright (c) 1997 Raul Garcia Garcia * Copyright (c) 1998-2001 Geoff Wing * - extensive modifications * Copyright (c) 1999 D J Hawkey Jr * - lastlog support * Copyright (c) 2004-2006 Marc Lehmann * Copyright (c) 2006-2021 Emanuele Giaquinta * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. *----------------------------------------------------------------------*/ #include "config.h" #include "ptytty.h" #include #include #include #include #include static void fill_id (char *id, const char *line, size_t id_size) { size_t len = strlen (line); if (len > id_size) line += len - id_size; strncpy (id, line, id_size); } /* * BSD style utmp entry * ut_line, ut_name, ut_host, ut_time * SYSV style utmp (and utmpx) entry * ut_user, ut_id, ut_line, ut_pid, ut_type, ut_exit, ut_time */ #if defined(USE_UTMPX) #include #include #if !defined(WTMPX_FILE) # if defined(_PATH_WTMPX) # define WTMPX_FILE _PATH_WTMPX # elif defined(PT_WTMPX_FILE) # define WTMPX_FILE PT_WTMPX_FILE # endif #endif #if !defined(LASTLOGX_FILE) # if defined(_PATH_LASTLOGX) # define LASTLOGX_FILE _PATH_LASTLOGX # elif defined(PT_LASTLOGX_FILE) # define LASTLOGX_FILE PT_LASTLOGX_FILE # endif #endif #ifdef LASTLOG_SUPPORT static void update_lastlog (const char *pty, const char *host) { # if defined(HAVE_STRUCT_LASTLOGX) && defined(HAVE_UPDLASTLOGX) struct lastlogx llx; memset (&llx, 0, sizeof (llx)); gettimeofday (&llx.ll_tv, 0); strncpy (llx.ll_line, pty, sizeof (llx.ll_line)); strncpy (llx.ll_host, host, sizeof (llx.ll_host)); updlastlogx (LASTLOGX_FILE, getuid (), &llx); # endif } #endif static void fill_utmpx (struct utmpx *utx, bool login, int pid, const char *line, const char *user, const char *host) { memset (utx, 0, sizeof (struct utmpx)); // posix says that ut_line is not meaningful for DEAD_PROCESS // records, but most implementations of last use ut_line to // associate records in wtmp file strncpy (utx->ut_line, line, sizeof (utx->ut_line)); fill_id (utx->ut_id, line, sizeof (utx->ut_id)); utx->ut_pid = pid; utx->ut_type = login ? USER_PROCESS : DEAD_PROCESS; gettimeofday (&utx->ut_tv, 0); // posix says that ut_user is not meaningful for DEAD_PROCESS // records, but solaris utmp_update helper requires that the ut_user // field of a DEAD_PROCESS entry matches the one of an existing // USER_PROCESS entry for the same line, if any strncpy (utx->ut_user, user, sizeof (utx->ut_user)); #ifdef HAVE_UTMPX_HOST if (login) strncpy (utx->ut_host, host, sizeof (utx->ut_host)); #endif } void ptytty_unix::log_session (bool login, const char *hostname) { struct passwd *pwent = getpwuid (getuid ()); const char *user = (pwent && pwent->pw_name) ? pwent->pw_name : "?"; const char *pty = name; if (!strncmp (pty, "/dev/", 5)) pty += 5; /* skip /dev/ prefix */ struct utmpx *tmputx; struct utmpx utx; fill_utmpx (&utx, login, cmd_pid, pty, user, hostname); setutxent (); if (login || ((tmputx = getutxid (&utx)) && tmputx->ut_pid == cmd_pid)) pututxline (&utx); endutxent (); if (login_shell) { #if defined(WTMP_SUPPORT) && defined(HAVE_UPDWTMPX) updwtmpx (WTMPX_FILE, &utx); #endif #ifdef LASTLOG_SUPPORT if (login) { if (pwent) update_lastlog (pty, hostname); } #endif } } #elif defined(USE_UTMP) #include #include #ifdef HAVE_LASTLOG_H # include #endif #if !defined(UTMP_FILE) # if defined(_PATH_UTMP) # define UTMP_FILE _PATH_UTMP # elif defined(PT_UTMP_FILE) # define UTMP_FILE PT_UTMP_FILE # endif #endif #if !defined(WTMP_FILE) # if defined(_PATH_WTMP) # define WTMP_FILE _PATH_WTMP # elif defined(PT_WTMP_FILE) # define WTMP_FILE PT_WTMP_FILE # endif #endif #if !defined(LASTLOG_FILE) # if defined(_PATH_LASTLOG) # define LASTLOG_FILE _PATH_LASTLOG # elif defined(PT_LASTLOG_FILE) # define LASTLOG_FILE PT_LASTLOG_FILE # endif #endif #include #include #include static void write_record (const char *path, off_t pos, const void *record, size_t size) { int fd = open (path, O_WRONLY); if (fd >= 0) { pwrite (fd, record, size, pos * size); close (fd); } } /* ------------------------------------------------------------------------- */ /* * Update a BSD style wtmp entry */ #if defined(WTMP_SUPPORT) && !defined(HAVE_UPDWTMP) static void updwtmp (const char *fname, const struct utmp *ut) { int fd, gotlock, retry; struct flock lck; /* fcntl locking scheme */ struct stat sbuf; if ((fd = open (fname, O_WRONLY | O_APPEND, 0)) < 0) return; lck.l_whence = SEEK_END; /* start lock at current eof */ lck.l_len = 0; /* end at ``largest possible eof'' */ lck.l_start = 0; lck.l_type = F_WRLCK; /* we want a write lock */ /* attempt lock with F_SETLK; F_SETLKW would cause a deadlock! */ for (retry = 10, gotlock = 0; retry--;) if (fcntl (fd, F_SETLK, &lck) != -1) { gotlock = 1; break; } else if (errno != EAGAIN && errno != EACCES) break; if (gotlock) { if (fstat (fd, &sbuf) == 0) if (write (fd, ut, sizeof (struct utmp)) != sizeof (struct utmp)) ftruncate (fd, sbuf.st_size); /* remove bad writes */ lck.l_type = F_UNLCK; /* unlocking the file */ fcntl (fd, F_SETLK, &lck); } close (fd); } #endif /* ------------------------------------------------------------------------- */ #ifdef LASTLOG_SUPPORT static void update_lastlog (const char *pty, const char *host) { # ifdef HAVE_STRUCT_LASTLOG struct lastlog ll; memset (&ll, 0, sizeof (ll)); ll.ll_time = time (NULL); strncpy (ll.ll_line, pty, sizeof (ll.ll_line)); strncpy (ll.ll_host, host, sizeof (ll.ll_host)); write_record (LASTLOG_FILE, getuid (), &ll, sizeof (ll)); # endif /* HAVE_STRUCT_LASTLOG */ } #endif /* LASTLOG_SUPPORT */ static void fill_utmp (struct utmp *ut, bool login, int pid, const char *line, const char *user, const char *host) { memset (ut, 0, sizeof (struct utmp)); strncpy (ut->ut_line, line, sizeof (ut->ut_line)); #ifdef HAVE_UTMP_PID fill_id (ut->ut_id, line, sizeof (ut->ut_id)); ut->ut_pid = pid; ut->ut_type = login ? USER_PROCESS : DEAD_PROCESS; #endif ut->ut_time = time (NULL); if (login) { #ifdef HAVE_UTMP_PID strncpy (ut->ut_user, user, sizeof (ut->ut_user)); #else strncpy (ut->ut_name, user, sizeof (ut->ut_name)); #endif #ifdef HAVE_UTMP_HOST strncpy (ut->ut_host, host, sizeof (ut->ut_host)); #endif } } void ptytty_unix::log_session (bool login, const char *hostname) { struct passwd *pwent = getpwuid (getuid ()); const char *user = (pwent && pwent->pw_name) ? pwent->pw_name : "?"; const char *pty = name; if (!strncmp (pty, "/dev/", 5)) pty += 5; /* skip /dev/ prefix */ struct utmp *tmput; struct utmp ut; fill_utmp (&ut, login, cmd_pid, pty, user, hostname); #ifdef HAVE_UTMP_PID setutent (); if (login || ((tmput = getutid (&ut)) && tmput->ut_pid == cmd_pid)) pututline (&ut); endutent (); #else if (utmp_pos > 0) write_record (UTMP_FILE, utmp_pos, &ut, sizeof (ut)); #endif if (login_shell) { #ifdef WTMP_SUPPORT updwtmp (WTMP_FILE, &ut); #endif #ifdef LASTLOG_SUPPORT if (login) { if (pwent) update_lastlog (pty, hostname); } #endif } } #endif libptytty-2.0/src/fdpass.C0000644000175000017500000000557614076743321015106 0ustar giaqemgiaqem/*----------------------------------------------------------------------* * File: fdpass.C *----------------------------------------------------------------------* * * All portions of code are copyright by their respective author/s. * Copyright (c) 2005-2006,2012 Marc Lehmann * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. *----------------------------------------------------------------------*/ #include "config.h" #include // needed by broken bsds for NULL used in sys/uio.h #include #include #include #include #include "libptytty.h" // CMSG_SPACE & CMSG_LEN are rfc2292 extensions to unix #ifndef CMSG_SPACE # define CMSG_SPACE(len) (sizeof (cmsghdr) + len) #endif #ifndef CMSG_LEN # define CMSG_LEN(len) (sizeof (cmsghdr) + len) #endif bool ptytty::send_fd (int socket, int fd) { void *buf = malloc (CMSG_SPACE (sizeof (int))); if (!buf) return 0; msghdr msg; iovec iov; cmsghdr *cmsg; char data = 0; iov.iov_base = &data; iov.iov_len = 1; msg.msg_name = 0; msg.msg_namelen = 0; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = buf; msg.msg_controllen = CMSG_SPACE (sizeof (int)); cmsg = CMSG_FIRSTHDR (&msg); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; cmsg->cmsg_len = CMSG_LEN (sizeof (int)); *(int *)CMSG_DATA (cmsg) = fd; ssize_t result = sendmsg (socket, &msg, 0); free (buf); return result >= 0; } int ptytty::recv_fd (int socket) { void *buf = malloc (CMSG_SPACE (sizeof (int))); if (!buf) return -1; msghdr msg; iovec iov; char data = 1; iov.iov_base = &data; iov.iov_len = 1; msg.msg_name = 0; msg.msg_namelen = 0; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = buf; msg.msg_controllen = CMSG_SPACE (sizeof (int)); int fd = -1; if (recvmsg (socket, &msg, 0) > 0 && data == 0) { cmsghdr *cmsg = CMSG_FIRSTHDR (&msg); if (cmsg && cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS && cmsg->cmsg_len >= CMSG_LEN (sizeof (int))) fd = *(int *)CMSG_DATA (cmsg); } free (buf); return fd; } libptytty-2.0/src/libptytty.h0000644000175000017500000000321114077050027015712 0ustar giaqemgiaqem#ifndef LIBPTYTTY_H_ /* public libptytty header file */ #define LIBPTYTTY_H_ #ifdef __cplusplus // C++ API #include #include #include class ptytty_error : public std::exception { private: char *msg; public: ptytty_error (const char *what_arg) { msg = strdup (what_arg); } ~ptytty_error () { free (msg); } const char * what () const noexcept override { return msg; } }; struct ptytty { int pty; // pty file descriptor; connected to terminal emulator int tty; // tty file descriptor; connected to child virtual ~ptytty () { } virtual bool get () = 0; virtual void login (int cmd_pid, bool login_shell, const char *hostname) = 0; void close_tty (); bool make_controlling_tty (); void set_utf8_mode (bool on); static void sanitise_stdfd (); static void init (); static ptytty *create (); // create a new pty object static void drop_privileges (); static void use_helper (); static bool send_fd (int socket, int fd); static int recv_fd (int socket); protected: ptytty () : pty(-1), tty(-1) { } }; #else // C API typedef void *PTYTTY; int ptytty_pty (PTYTTY ptytty); int ptytty_tty (PTYTTY ptytty); void ptytty_delete (PTYTTY ptytty); int ptytty_get (PTYTTY ptytty); void ptytty_login (PTYTTY ptytty, int cmd_pid, int login_shell, const char *hostname); void ptytty_close_tty (PTYTTY ptytty); int ptytty_make_controlling_tty (PTYTTY ptytty); void ptytty_set_utf8_mode (PTYTTY ptytty, int on); void ptytty_sanitise_stdfd (); void ptytty_init (); PTYTTY ptytty_create (); void ptytty_drop_privileges (); void ptytty_use_helper (); #endif #endif libptytty-2.0/src/c-api.C0000644000175000017500000000477314076743321014615 0ustar giaqemgiaqem/*----------------------------------------------------------------------* * File: c-api.C *----------------------------------------------------------------------* * * All portions of code are copyright by their respective author/s. * Copyright (c) 2005 Marc Lehmann * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. *---------------------------------------------------------------------*/ #include "config.h" #include "ptytty.h" ///////////////////////////////////////////////////////////////////////////// // C API #if PTYTTY_C_API typedef void *PTYTTY; extern "C" { int ptytty_pty (PTYTTY ptytty) { return ((struct ptytty *)ptytty)->pty; } int ptytty_tty (PTYTTY ptytty) { return ((struct ptytty *)ptytty)->tty; } int ptytty_get (PTYTTY ptytty) { return ((struct ptytty *)ptytty)->get (); } void ptytty_login (PTYTTY ptytty, int cmd_pid, int login_shell, const char *hostname) { return ((struct ptytty *)ptytty)->login (cmd_pid, login_shell, hostname); } void ptytty_close_tty (PTYTTY ptytty) { return ((struct ptytty *)ptytty)->close_tty (); } int ptytty_make_controlling_tty (PTYTTY ptytty) { return ((struct ptytty *)ptytty)->make_controlling_tty (); } void ptytty_set_utf8_mode (PTYTTY ptytty, int on) { return ((struct ptytty *)ptytty)->set_utf8_mode (on); } void ptytty_sanitise_stdfd () { return ptytty::sanitise_stdfd (); } void ptytty_init () { return ptytty::init (); } PTYTTY ptytty_create () { return ptytty::create (); } void ptytty_delete (PTYTTY ptytty) { delete (struct ptytty *)ptytty; } void ptytty_drop_privileges () { return ptytty::drop_privileges (); } #if PTYTTY_HELPER void ptytty_use_helper () { return ptytty::use_helper (); } #endif } // send_fd, recv_fd not exposed #endif libptytty-2.0/src/ptytty.C0000644000175000017500000002223214102517460015160 0ustar giaqemgiaqem/*----------------------------------------------------------------------* * File: ptytty.C *----------------------------------------------------------------------* * * All portions of code are copyright by their respective author/s. * Copyright (c) 1999-2001 Geoff Wing * Copyright (c) 2004-2006 Marc Lehmann * Copyright (c) 2006 Emanuele Giaquinta * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. *---------------------------------------------------------------------*/ #include "config.h" #include "ptytty.h" #include #include #include #include #include #include #include #ifdef HAVE_SYS_IOCTL_H # include #endif #ifdef HAVE_STROPTS_H # include #endif #if defined(HAVE_PTY_H) # include #elif defined(HAVE_LIBUTIL_H) # include #elif defined(HAVE_UTIL_H) # include #endif #ifdef TTY_GID_SUPPORT #include #endif #ifndef O_NOCTTY # define O_NOCTTY 0 #endif ///////////////////////////////////////////////////////////////////////////// /* ------------------------------------------------------------------------- * * GET PSEUDO TELETYPE - MASTER AND SLAVE * * ------------------------------------------------------------------------- */ /* * Returns pty file descriptor, or -1 on failure * If successful, ttydev is set to the name of the slave device. * fd_tty _may_ also be set to an open fd to the slave device */ #if defined(UNIX98_PTY) static int get_pty (int *fd_tty, char **ttydev) { int pfd; # if defined(HAVE_GETPT) pfd = getpt (); # elif defined(HAVE_POSIX_OPENPT) pfd = posix_openpt (O_RDWR | O_NOCTTY); # else # ifdef _AIX pfd = open ("/dev/ptc", O_RDWR | O_NOCTTY, 0); # else pfd = open ("/dev/ptmx", O_RDWR | O_NOCTTY, 0); # endif # endif if (pfd >= 0) { if (grantpt (pfd) == 0 /* change slave permissions */ && unlockpt (pfd) == 0) { /* slave now unlocked */ *ttydev = strdup (ptsname (pfd)); /* get slave's name */ return pfd; } close (pfd); } return -1; } #elif defined(HAVE_OPENPTY) static int get_pty (int *fd_tty, char **ttydev) { int pfd; int res; res = openpty (&pfd, fd_tty, NULL, NULL, NULL); if (res != -1) { *ttydev = strdup (ttyname (*fd_tty)); return pfd; } return -1; } #elif defined(HAVE__GETPTY) static int get_pty (int *fd_tty, char **ttydev) { int pfd; char *slave; slave = _getpty (&pfd, O_RDWR | O_NOCTTY, 0622, 0); if (slave != NULL) { *ttydev = strdup (slave); return pfd; } return -1; } #else # define SET_TTY_OWNER /* Based on the code in openssh/openbsd-compat/bsd-openpty.c */ static int get_pty (int *fd_tty, char **ttydev) { int pfd; int i; char pty_name[32]; char tty_name[32]; const char *majors = "pqrstuvwxyzabcde"; const char *minors = "0123456789abcdef"; for (i = 0; i < 256; i++) { snprintf (pty_name, 32, "/dev/pty%c%c", majors[i / 16], minors[i % 16]); snprintf (tty_name, 32, "/dev/tty%c%c", majors[i / 16], minors[i % 16]); if ((pfd = open (pty_name, O_RDWR | O_NOCTTY, 0)) == -1) { snprintf (pty_name, 32, "/dev/ptyp%d", i); snprintf (tty_name, 32, "/dev/ttyp%d", i); if ((pfd = open (pty_name, O_RDWR | O_NOCTTY, 0)) == -1) continue; } if (access (tty_name, R_OK | W_OK) == 0) { *ttydev = strdup (tty_name); return pfd; } close (pfd); } return -1; } #endif /*----------------------------------------------------------------------*/ /* * Returns tty file descriptor, or -1 on failure */ static int get_tty (char *ttydev) { return open (ttydev, O_RDWR | O_NOCTTY, 0); } /*----------------------------------------------------------------------*/ /* * Make our tty a controlling tty so that /dev/tty points to us */ static int control_tty (int fd_tty) { int fd; setsid (); #ifdef TIOCSCTTY ioctl (fd_tty, TIOCSCTTY, NULL); #else fd = open (ttyname (fd_tty), O_RDWR); if (fd >= 0) close (fd); #endif fd = open ("/dev/tty", O_WRONLY); if (fd < 0) return -1; /* fatal */ close (fd); return 0; } void ptytty::close_tty () { if (tty < 0) return; close (tty); tty = -1; } bool ptytty::make_controlling_tty () { return control_tty (tty) >= 0; } void ptytty::set_utf8_mode (bool on) { #ifdef IUTF8 if (pty < 0) return; struct termios tio; if (tcgetattr (pty, &tio) != -1) { tcflag_t new_cflag = tio.c_iflag; if (on) new_cflag |= IUTF8; else new_cflag &= ~IUTF8; if (new_cflag != tio.c_iflag) { tio.c_iflag = new_cflag; tcsetattr (pty, TCSANOW, &tio); } } #endif } static struct ttyconf { gid_t gid; mode_t mode; ttyconf () { #ifdef TTY_GID_SUPPORT struct group *gr = getgrnam ("tty"); if (gr) { /* change group ownership of tty to "tty" */ mode = S_IRUSR | S_IWUSR | S_IWGRP; gid = gr->gr_gid; } else #endif /* TTY_GID_SUPPORT */ { mode = S_IRUSR | S_IWUSR | S_IWGRP | S_IWOTH; gid = 0; } } } ttyconf; ptytty_unix::ptytty_unix () { name = 0; #if UTMP_SUPPORT cmd_pid = 0; #endif } ptytty_unix::~ptytty_unix () { #if UTMP_SUPPORT if (cmd_pid) log_session (false, 0); #endif put (); } void ptytty_unix::put () { if (name) { chmod (name, RESTORE_TTY_MODE); chown (name, 0, ttyconf.gid); } close_tty (); if (pty >= 0) close (pty); free (name); pty = tty = -1; name = 0; } bool ptytty_unix::get () { /* get master (pty) */ if ((pty = get_pty (&tty, &name)) < 0) return false; /* get slave (tty) */ if (tty < 0) { #ifdef SET_TTY_OWNER chown (name, getuid (), ttyconf.gid); /* fail silently */ chmod (name, ttyconf.mode); # ifdef HAVE_REVOKE revoke (name); # endif #endif if ((tty = get_tty (name)) < 0) { put (); return false; } } #if defined(I_PUSH) /* * Push STREAMS modules: * ptem: pseudo-terminal hardware emulation module. * ldterm: standard terminal line discipline. * ttcompat: V7, 4BSD and XENIX STREAMS compatibility module. * * On Solaris, a process can acquire a controlling terminal in the * following ways: * - open() of /dev/ptmx or of a slave device without O_NOCTTY * - I_PUSH ioctl() of the "ptem" or "ldterm" module on a slave device * The second case is problematic, because it cannot be disabled. * Fortunately, Solaris (10 and 11 at least) provides an undocumented * __IPUSH_NOCTTY ioctl which does not have this side-effect, so we * use it if defined. See * https://github.com/illumos/illumos-gate/blob/19d32b9ab53d17ac6605971e14c45a5281f8d9bb/usr/src/uts/common/os/streamio.c#L3755 * https://github.com/illumos/illumos-gate/blob/19d32b9ab53d17ac6605971e14c45a5281f8d9bb/usr/src/uts/common/io/ptem.c#L203 * https://github.com/illumos/illumos-gate/blob/19d32b9ab53d17ac6605971e14c45a5281f8d9bb/usr/src/uts/common/io/ldterm.c#L794 * Note that an open() of a slave device autoloads the modules, * with __I_PUSH_NOCTTY, if xpg[46] mode is enabled (which requires * linking /usr/lib/values-xpg[46].o). * https://github.com/illumos/illumos-gate/blob/19d32b9ab53d17ac6605971e14c45a5281f8d9bb/usr/src/lib/libc/port/sys/open.c#L173 */ #ifdef __I_PUSH_NOCTTY # define PT_I_PUSH __I_PUSH_NOCTTY #else # define PT_I_PUSH I_PUSH #endif #if defined(HAVE_ISASTREAM) && defined(HAVE_STROPTS_H) if (isastream (tty) == 1) # endif { if (!ioctl (tty, I_FIND, "ptem")) ioctl (tty, PT_I_PUSH, "ptem"); if (!ioctl (tty, I_FIND, "ldterm")) ioctl (tty, PT_I_PUSH, "ldterm"); if (!ioctl (tty, I_FIND, "ttcompat")) ioctl (tty, PT_I_PUSH, "ttcompat"); } #endif #if defined(USE_UTMP) && !defined(HAVE_UTMP_PID) int fd_stdin = dup (STDIN_FILENO); dup2 (tty, STDIN_FILENO); utmp_pos = ttyslot (); dup2 (fd_stdin, STDIN_FILENO); close (fd_stdin); #endif return true; } void ptytty_unix::login (int cmd_pid, bool login_shell, const char *hostname) { #if UTMP_SUPPORT if (!name || !*name) return; this->cmd_pid = cmd_pid; this->login_shell = login_shell; log_session (true, hostname); #endif } libptytty-2.0/src/ptytty_conf.h0000644000175000017500000000217614076743321016246 0ustar giaqemgiaqem/* * This file contains a few tunables for libptytty. Normally there should * be little reason to change this file. It is provided to suit rare or * special cases only. */ /* * Default mode to restore when releasing the PTS device. It is relaxed to be * compatible with most systems, change it to a more secure value if your * system supports it (0640 for example). */ #ifndef RESTORE_TTY_MODE # define RESTORE_TTY_MODE 0666 #endif /* * Define if you want to enable the C api. */ #ifndef PTYTTY_C_API # define PTYTTY_C_API 1 #endif /* * Define if you want to use a separate process for pty/tty handling * when running setuid/setgid. You need this when making it setuid/setgid. */ #ifndef PTYTTY_HELPER # define PTYTTY_HELPER 1 #endif /* * Define if you want to use a single helper process from multiple * threads OR forked processes. Without it, the user is responsible for * serialising all calls to libptytty functions. Having it disabled * avoids some syscalls and reduces codesize, but unless you are really * short on cpu or memory, it's not worth disabling. */ #ifndef PTYTTY_REENTRANT # define PTYTTY_REENTRANT 1 #endif libptytty-2.0/src/estl.h0000644000175000017500000002214714076743321014633 0ustar giaqemgiaqem/* this file lives in libptytty/src/estl.h, only edit it there and copy it here */ #ifndef ESTL_H_ #define ESTL_H_ #include #include #include "ecb.h" template static inline T min (T a, U b) { return a < (T)b ? a : (T)b; } template static inline T max (T a, U b) { return a > (T)b ? a : (T)b; } template static inline void swap (T& a, U& b) { T t = a; a = (T)b; b = (U)t; } template I find (I first, I last, const T& value) { while (first != last && *first != value) ++first; return first; } #include #if ECB_CPP11 #include #endif namespace estl { #if ESTL_LARGE_MEMORY_MODEL // should use size_t/ssize_t, but that's not portable enough for us typedef unsigned long size_type; typedef long difference_type; #else typedef uint32_t size_type; typedef int32_t difference_type; #endif template struct scoped_ptr { T *p; scoped_ptr () : p (0) { } explicit scoped_ptr (T *a) : p (a) { } ~scoped_ptr () { delete p; } void reset (T *a) { delete p; p = a; } T *operator ->() const { return p; } T &operator *() const { return *p; } operator T *() { return p; } T *get () const { return p; } private: scoped_ptr (const scoped_ptr &); scoped_ptr &operator =(const scoped_ptr &); }; template struct scoped_array { T *p; scoped_array () : p (0) { } explicit scoped_array (T *a) : p (a) { } ~scoped_array () { delete [] p; } void reset (T *a) { delete [] p; p = a; } operator T *() { return p; } T *get () const { return p; } private: scoped_array (const scoped_array &); scoped_array &operator =(const scoped_array &); }; } // original version taken from MICO, but this has been completely rewritten // known limitations w.r.t. std::vector // - many methods missing // - no error checking, no exceptions thrown (e.g. at()) // - size_type is 32bit even on 64 bit hosts, so limited to 2**31 elements // - no allocator support // - we don't really care about const correctness, but we try // - we don't care about namespaces and stupid macros the user might define // - no bool specialisation template struct simplevec { typedef estl::size_type size_type; typedef T value_type; typedef T *iterator; typedef const T *const_iterator; typedef T *pointer; typedef const T *const_pointer; typedef T &reference; typedef const T &const_reference; // missing: allocator_type // missing: reverse iterator private: size_type sze, res; T *buf; // we shamelessly optimise for "simple" types. everything // "not simple enough" will use the slow path. static bool is_simple_enough () { #if ECB_CPP11 return std::is_trivially_assignable::value && std::is_trivially_constructible::value && std::is_trivially_copyable::value && std::is_trivially_destructible::value; #elif ECB_GCC_VERSION(4,4) || ECB_CLANG_VERSION(2,8) return __has_trivial_assign (T) && __has_trivial_constructor (T) && __has_trivial_copy (T) && __has_trivial_destructor (T); #else return 0; #endif } static void construct (iterator a, size_type n = 1) { if (!is_simple_enough ()) while (n--) new (a++) T (); } static void destruct (iterator a, size_type n = 1) { if (!is_simple_enough ()) while (n--) (*a++).~T (); } template static void cop_new (iterator a, I b) { new (a) T (*b); } template static void cop_set (iterator a, I b) { *a = *b ; } // MUST copy forwards template static void copy (iterator dst, I src, size_type n, void (*op)(iterator, I)) { while (n--) op (dst++, src++); } static void copy (iterator dst, iterator src, size_type n, void (*op)(iterator, iterator)) { if (is_simple_enough ()) memcpy (dst, src, sizeof (T) * n); else copy (dst, src, n, op); } static T *alloc (size_type n) ecb_cold { return (T *)::operator new ((size_t) (sizeof (T) * n)); } void dealloc () ecb_cold { destruct (buf, sze); ::operator delete (buf); } size_type good_size (size_type n) ecb_cold { return n ? 2UL << ecb_ld32 (n) : 5; } void ins (iterator where, size_type n) { size_type pos = where - begin (); if (ecb_expect_false (sze + n > res)) { res = good_size (sze + n); T *nbuf = alloc (res); copy (nbuf, buf, sze, cop_new); dealloc (); buf = nbuf; } construct (buf + sze, n); iterator src = buf + pos; if (is_simple_enough ()) memmove (src + n, src, sizeof (T) * (sze - pos)); else for (size_type i = sze - pos; i--; ) cop_set (src + n + i, src + i); sze += n; } public: size_type capacity () const { return res; } size_type size () const { return sze; } bool empty () const { return size () == 0; } size_t max_size () const { return (~(size_type)0) >> 1; } const_iterator begin () const { return &buf [ 0]; } iterator begin () { return &buf [ 0]; } const_iterator end () const { return &buf [sze ]; } iterator end () { return &buf [sze ]; } const_reference front () const { return buf [ 0]; } reference front () { return buf [ 0]; } const_reference back () const { return buf [sze - 1]; } reference back () { return buf [sze - 1]; } void reserve (size_type sz) { if (ecb_expect_true (sz <= res)) return; sz = good_size (sz); T *nbuf = alloc (sz); copy (nbuf, begin (), sze, cop_new); dealloc (); buf = nbuf; res = sz; } void resize (size_type sz) { reserve (sz); if (is_simple_enough ()) sze = sz; else { while (sze < sz) construct (buf + sze++); while (sze > sz) destruct (buf + --sze); } } simplevec () : sze(0), res(0), buf(0) { } simplevec (size_type n, const T &t = T ()) { sze = res = n; buf = alloc (sze); while (n--) new (buf + n) T (t); } simplevec (const_iterator first, const_iterator last) { sze = res = last - first; buf = alloc (sze); copy (buf, first, sze, cop_new); } simplevec (const simplevec &v) : sze(0), res(0), buf(0) { sze = res = v.size (); buf = alloc (sze); copy (buf, v.begin (), sze, cop_new); } ~simplevec () { dealloc (); } void swap (simplevec &t) { ::swap (sze, t.sze); ::swap (res, t.res); ::swap (buf, t.buf); } void clear () { destruct (buf, sze); sze = 0; } void push_back (const T &t) { reserve (sze + 1); new (buf + sze++) T (t); } void pop_back () { destruct (buf + --sze); } const_reference operator [](size_type idx) const { return buf[idx]; } reference operator [](size_type idx) { return buf[idx]; } const_reference at (size_type idx) const { return buf [idx]; } reference at (size_type idx) { return buf [idx]; } void assign (const_iterator first, const_iterator last) { simplevec v (first, last); swap (v); } void assign (size_type n, const T &t) { simplevec v (n, t); swap (v); } simplevec &operator= (const simplevec &v) { assign (v.begin (), v.end ()); return *this; } iterator insert (iterator pos, const T &t) { size_type at = pos - begin (); ins (pos, 1); buf [at] = t; return buf + at; } iterator insert (iterator pos, const_iterator first, const_iterator last) { size_type n = last - first; size_type at = pos - begin (); ins (pos, n); copy (buf + at, first, n, cop_set); return buf + at; } iterator insert (iterator pos, size_type n, const T &t) { size_type at = pos - begin (); ins (pos, n); for (iterator i = buf + at; n--; ) *i++ = t; return buf + at; } iterator erase (iterator first, iterator last) { size_type n = last - first; size_type c = end () - last; if (is_simple_enough ()) memmove (first, last, sizeof (T) * c); else copy (first, last, c, cop_set); sze -= n; destruct (buf + sze, n); return first; } iterator erase (iterator pos) { if (pos != end ()) erase (pos, pos + 1); return pos; } }; template bool operator ==(const simplevec &v1, const simplevec &v2) { if (v1.size () != v2.size ()) return false; return !v1.size () || !memcmp (&v1[0], &v2[0], v1.size () * sizeof (T)); } template bool operator <(const simplevec &v1, const simplevec &v2) { unsigned long minlast = min (v1.size (), v2.size ()); for (unsigned long i = 0; i < minlast; ++i) { if (v1[i] < v2[i]) return true; if (v2[i] < v1[i]) return false; } return v1.size () < v2.size (); } template struct vector : simplevec { }; #endif libptytty-2.0/src/ecb.h0000644000175000017500000014446414076743321014424 0ustar giaqemgiaqem/* * libecb - http://software.schmorp.de/pkg/libecb * * Copyright (©) 2009-2015,2018-2021 Marc Alexander Lehmann * Copyright (©) 2011 Emanuele Giaquinta * All rights reserved. * * Redistribution and use in source and binary forms, with or without modifica- * tion, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License ("GPL") version 2 or any later version, * in which case the provisions of the GPL are applicable instead of * the above. If you wish to allow the use of your version of this file * only under the terms of the GPL and not to allow others to use your * version of this file under the BSD license, indicate your decision * by deleting the provisions above and replace them with the notice * and other provisions required by the GPL. If you do not delete the * provisions above, a recipient may use your version of this file under * either the BSD or the GPL. */ #ifndef ECB_H #define ECB_H /* 16 bits major, 16 bits minor */ #define ECB_VERSION 0x00010009 #include /* for memcpy */ #if defined (_WIN32) && !defined (__MINGW32__) typedef signed char int8_t; typedef unsigned char uint8_t; typedef signed char int_fast8_t; typedef unsigned char uint_fast8_t; typedef signed short int16_t; typedef unsigned short uint16_t; typedef signed int int_fast16_t; typedef unsigned int uint_fast16_t; typedef signed int int32_t; typedef unsigned int uint32_t; typedef signed int int_fast32_t; typedef unsigned int uint_fast32_t; #if __GNUC__ typedef signed long long int64_t; typedef unsigned long long uint64_t; #else /* _MSC_VER || __BORLANDC__ */ typedef signed __int64 int64_t; typedef unsigned __int64 uint64_t; #endif typedef int64_t int_fast64_t; typedef uint64_t uint_fast64_t; #ifdef _WIN64 #define ECB_PTRSIZE 8 typedef uint64_t uintptr_t; typedef int64_t intptr_t; #else #define ECB_PTRSIZE 4 typedef uint32_t uintptr_t; typedef int32_t intptr_t; #endif #else #include #if (defined INTPTR_MAX ? INTPTR_MAX : ULONG_MAX) > 0xffffffffU #define ECB_PTRSIZE 8 #else #define ECB_PTRSIZE 4 #endif #endif #define ECB_GCC_AMD64 (__amd64 || __amd64__ || __x86_64 || __x86_64__) #define ECB_MSVC_AMD64 (_M_AMD64 || _M_X64) #ifndef ECB_OPTIMIZE_SIZE #if __OPTIMIZE_SIZE__ #define ECB_OPTIMIZE_SIZE 1 #else #define ECB_OPTIMIZE_SIZE 0 #endif #endif /* work around x32 idiocy by defining proper macros */ #if ECB_GCC_AMD64 || ECB_MSVC_AMD64 #if _ILP32 #define ECB_AMD64_X32 1 #else #define ECB_AMD64 1 #endif #endif #if ECB_PTRSIZE >= 8 || ECB_AMD64_X32 #define ECB_64BIT_NATIVE 1 #else #define ECB_64BIT_NATIVE 0 #endif /* many compilers define _GNUC_ to some versions but then only implement * what their idiot authors think are the "more important" extensions, * causing enormous grief in return for some better fake benchmark numbers. * or so. * we try to detect these and simply assume they are not gcc - if they have * an issue with that they should have done it right in the first place. */ #if !defined __GNUC_MINOR__ || defined __INTEL_COMPILER || defined __SUNPRO_C || defined __SUNPRO_CC || defined __llvm__ || defined __clang__ #define ECB_GCC_VERSION(major,minor) 0 #else #define ECB_GCC_VERSION(major,minor) (__GNUC__ > (major) || (__GNUC__ == (major) && __GNUC_MINOR__ >= (minor))) #endif #define ECB_CLANG_VERSION(major,minor) (__clang_major__ > (major) || (__clang_major__ == (major) && __clang_minor__ >= (minor))) #if __clang__ && defined __has_builtin #define ECB_CLANG_BUILTIN(x) __has_builtin (x) #else #define ECB_CLANG_BUILTIN(x) 0 #endif #if __clang__ && defined __has_extension #define ECB_CLANG_EXTENSION(x) __has_extension (x) #else #define ECB_CLANG_EXTENSION(x) 0 #endif #define ECB_CPP (__cplusplus+0) #define ECB_CPP11 (__cplusplus >= 201103L) #define ECB_CPP14 (__cplusplus >= 201402L) #define ECB_CPP17 (__cplusplus >= 201703L) #if ECB_CPP #define ECB_C 0 #define ECB_STDC_VERSION 0 #else #define ECB_C 1 #define ECB_STDC_VERSION __STDC_VERSION__ #endif #define ECB_C99 (ECB_STDC_VERSION >= 199901L) #define ECB_C11 (ECB_STDC_VERSION >= 201112L) #define ECB_C17 (ECB_STDC_VERSION >= 201710L) #if ECB_CPP #define ECB_EXTERN_C extern "C" #define ECB_EXTERN_C_BEG ECB_EXTERN_C { #define ECB_EXTERN_C_END } #else #define ECB_EXTERN_C extern #define ECB_EXTERN_C_BEG #define ECB_EXTERN_C_END #endif /*****************************************************************************/ /* ECB_NO_THREADS - ecb is not used by multiple threads, ever */ /* ECB_NO_SMP - ecb might be used in multiple threads, but only on a single cpu */ #if ECB_NO_THREADS #define ECB_NO_SMP 1 #endif #if ECB_NO_SMP #define ECB_MEMORY_FENCE do { } while (0) #endif /* http://www-01.ibm.com/support/knowledgecenter/SSGH3R_13.1.0/com.ibm.xlcpp131.aix.doc/compiler_ref/compiler_builtins.html */ #if __xlC__ && ECB_CPP #include #endif #if 1400 <= _MSC_VER #include /* fence functions _ReadBarrier, also bit search functions _BitScanReverse */ #endif #ifndef ECB_MEMORY_FENCE #if ECB_GCC_VERSION(2,5) || defined __INTEL_COMPILER || (__llvm__ && __GNUC__) || __SUNPRO_C >= 0x5110 || __SUNPRO_CC >= 0x5110 #define ECB_MEMORY_FENCE_RELAXED __asm__ __volatile__ ("" : : : "memory") #if __i386 || __i386__ #define ECB_MEMORY_FENCE __asm__ __volatile__ ("lock; orb $0, -1(%%esp)" : : : "memory") #define ECB_MEMORY_FENCE_ACQUIRE __asm__ __volatile__ ("" : : : "memory") #define ECB_MEMORY_FENCE_RELEASE __asm__ __volatile__ ("" : : : "memory") #elif ECB_GCC_AMD64 #define ECB_MEMORY_FENCE __asm__ __volatile__ ("mfence" : : : "memory") #define ECB_MEMORY_FENCE_ACQUIRE __asm__ __volatile__ ("" : : : "memory") #define ECB_MEMORY_FENCE_RELEASE __asm__ __volatile__ ("" : : : "memory") #elif __powerpc__ || __ppc__ || __powerpc64__ || __ppc64__ #define ECB_MEMORY_FENCE __asm__ __volatile__ ("sync" : : : "memory") #elif defined __ARM_ARCH_2__ \ || defined __ARM_ARCH_3__ || defined __ARM_ARCH_3M__ \ || defined __ARM_ARCH_4__ || defined __ARM_ARCH_4T__ \ || defined __ARM_ARCH_5__ || defined __ARM_ARCH_5E__ \ || defined __ARM_ARCH_5T__ || defined __ARM_ARCH_5TE__ \ || defined __ARM_ARCH_5TEJ__ /* should not need any, unless running old code on newer cpu - arm doesn't support that */ #elif defined __ARM_ARCH_6__ || defined __ARM_ARCH_6J__ \ || defined __ARM_ARCH_6K__ || defined __ARM_ARCH_6ZK__ \ || defined __ARM_ARCH_6T2__ #define ECB_MEMORY_FENCE __asm__ __volatile__ ("mcr p15,0,%0,c7,c10,5" : : "r" (0) : "memory") #elif defined __ARM_ARCH_7__ || defined __ARM_ARCH_7A__ \ || defined __ARM_ARCH_7R__ || defined __ARM_ARCH_7M__ #define ECB_MEMORY_FENCE __asm__ __volatile__ ("dmb" : : : "memory") #elif __aarch64__ #define ECB_MEMORY_FENCE __asm__ __volatile__ ("dmb ish" : : : "memory") #elif (__sparc || __sparc__) && !(__sparc_v8__ || defined __sparcv8) #define ECB_MEMORY_FENCE __asm__ __volatile__ ("membar #LoadStore | #LoadLoad | #StoreStore | #StoreLoad" : : : "memory") #define ECB_MEMORY_FENCE_ACQUIRE __asm__ __volatile__ ("membar #LoadStore | #LoadLoad" : : : "memory") #define ECB_MEMORY_FENCE_RELEASE __asm__ __volatile__ ("membar #LoadStore | #StoreStore") #elif defined __s390__ || defined __s390x__ #define ECB_MEMORY_FENCE __asm__ __volatile__ ("bcr 15,0" : : : "memory") #elif defined __mips__ /* GNU/Linux emulates sync on mips1 architectures, so we force its use */ /* anybody else who still uses mips1 is supposed to send in their version, with detection code. */ #define ECB_MEMORY_FENCE __asm__ __volatile__ (".set mips2; sync; .set mips0" : : : "memory") #elif defined __alpha__ #define ECB_MEMORY_FENCE __asm__ __volatile__ ("mb" : : : "memory") #elif defined __hppa__ #define ECB_MEMORY_FENCE __asm__ __volatile__ ("" : : : "memory") #define ECB_MEMORY_FENCE_RELEASE __asm__ __volatile__ ("") #elif defined __ia64__ #define ECB_MEMORY_FENCE __asm__ __volatile__ ("mf" : : : "memory") #elif defined __m68k__ #define ECB_MEMORY_FENCE __asm__ __volatile__ ("" : : : "memory") #elif defined __m88k__ #define ECB_MEMORY_FENCE __asm__ __volatile__ ("tb1 0,%%r0,128" : : : "memory") #elif defined __sh__ #define ECB_MEMORY_FENCE __asm__ __volatile__ ("" : : : "memory") #endif #endif #endif #ifndef ECB_MEMORY_FENCE #if ECB_GCC_VERSION(4,7) /* see comment below (stdatomic.h) about the C11 memory model. */ #define ECB_MEMORY_FENCE __atomic_thread_fence (__ATOMIC_SEQ_CST) #define ECB_MEMORY_FENCE_ACQUIRE __atomic_thread_fence (__ATOMIC_ACQUIRE) #define ECB_MEMORY_FENCE_RELEASE __atomic_thread_fence (__ATOMIC_RELEASE) #undef ECB_MEMORY_FENCE_RELAXED #define ECB_MEMORY_FENCE_RELAXED __atomic_thread_fence (__ATOMIC_RELAXED) #elif ECB_CLANG_EXTENSION(c_atomic) /* see comment below (stdatomic.h) about the C11 memory model. */ #define ECB_MEMORY_FENCE __c11_atomic_thread_fence (__ATOMIC_SEQ_CST) #define ECB_MEMORY_FENCE_ACQUIRE __c11_atomic_thread_fence (__ATOMIC_ACQUIRE) #define ECB_MEMORY_FENCE_RELEASE __c11_atomic_thread_fence (__ATOMIC_RELEASE) #undef ECB_MEMORY_FENCE_RELAXED #define ECB_MEMORY_FENCE_RELAXED __c11_atomic_thread_fence (__ATOMIC_RELAXED) #elif ECB_GCC_VERSION(4,4) || defined __INTEL_COMPILER || defined __clang__ #define ECB_MEMORY_FENCE __sync_synchronize () #elif _MSC_VER >= 1500 /* VC++ 2008 */ /* apparently, microsoft broke all the memory barrier stuff in Visual Studio 2008... */ #pragma intrinsic(_ReadBarrier,_WriteBarrier,_ReadWriteBarrier) #define ECB_MEMORY_FENCE _ReadWriteBarrier (); MemoryBarrier() #define ECB_MEMORY_FENCE_ACQUIRE _ReadWriteBarrier (); MemoryBarrier() /* according to msdn, _ReadBarrier is not a load fence */ #define ECB_MEMORY_FENCE_RELEASE _WriteBarrier (); MemoryBarrier() #elif _MSC_VER >= 1400 /* VC++ 2005 */ #pragma intrinsic(_ReadBarrier,_WriteBarrier,_ReadWriteBarrier) #define ECB_MEMORY_FENCE _ReadWriteBarrier () #define ECB_MEMORY_FENCE_ACQUIRE _ReadWriteBarrier () /* according to msdn, _ReadBarrier is not a load fence */ #define ECB_MEMORY_FENCE_RELEASE _WriteBarrier () #elif defined _WIN32 #include #define ECB_MEMORY_FENCE MemoryBarrier () /* actually just xchg on x86... scary */ #elif __SUNPRO_C >= 0x5110 || __SUNPRO_CC >= 0x5110 #include #define ECB_MEMORY_FENCE __machine_rw_barrier () #define ECB_MEMORY_FENCE_ACQUIRE __machine_acq_barrier () #define ECB_MEMORY_FENCE_RELEASE __machine_rel_barrier () #define ECB_MEMORY_FENCE_RELAXED __compiler_barrier () #elif __xlC__ #define ECB_MEMORY_FENCE __sync () #endif #endif #ifndef ECB_MEMORY_FENCE #if ECB_C11 && !defined __STDC_NO_ATOMICS__ /* we assume that these memory fences work on all variables/all memory accesses, */ /* not just C11 atomics and atomic accesses */ #include #define ECB_MEMORY_FENCE atomic_thread_fence (memory_order_seq_cst) #define ECB_MEMORY_FENCE_ACQUIRE atomic_thread_fence (memory_order_acquire) #define ECB_MEMORY_FENCE_RELEASE atomic_thread_fence (memory_order_release) #endif #endif #ifndef ECB_MEMORY_FENCE #if !ECB_AVOID_PTHREADS /* * if you get undefined symbol references to pthread_mutex_lock, * or failure to find pthread.h, then you should implement * the ECB_MEMORY_FENCE operations for your cpu/compiler * OR provide pthread.h and link against the posix thread library * of your system. */ #include #define ECB_NEEDS_PTHREADS 1 #define ECB_MEMORY_FENCE_NEEDS_PTHREADS 1 static pthread_mutex_t ecb_mf_lock = PTHREAD_MUTEX_INITIALIZER; #define ECB_MEMORY_FENCE do { pthread_mutex_lock (&ecb_mf_lock); pthread_mutex_unlock (&ecb_mf_lock); } while (0) #endif #endif #if !defined ECB_MEMORY_FENCE_ACQUIRE && defined ECB_MEMORY_FENCE #define ECB_MEMORY_FENCE_ACQUIRE ECB_MEMORY_FENCE #endif #if !defined ECB_MEMORY_FENCE_RELEASE && defined ECB_MEMORY_FENCE #define ECB_MEMORY_FENCE_RELEASE ECB_MEMORY_FENCE #endif #if !defined ECB_MEMORY_FENCE_RELAXED && defined ECB_MEMORY_FENCE #define ECB_MEMORY_FENCE_RELAXED ECB_MEMORY_FENCE /* very heavy-handed */ #endif /*****************************************************************************/ #if ECB_CPP #define ecb_inline static inline #elif ECB_GCC_VERSION(2,5) #define ecb_inline static __inline__ #elif ECB_C99 #define ecb_inline static inline #else #define ecb_inline static #endif #if ECB_GCC_VERSION(3,3) #define ecb_restrict __restrict__ #elif ECB_C99 #define ecb_restrict restrict #else #define ecb_restrict #endif typedef int ecb_bool; #define ECB_CONCAT_(a, b) a ## b #define ECB_CONCAT(a, b) ECB_CONCAT_(a, b) #define ECB_STRINGIFY_(a) # a #define ECB_STRINGIFY(a) ECB_STRINGIFY_(a) #define ECB_STRINGIFY_EXPR(expr) ((expr), ECB_STRINGIFY_ (expr)) #define ecb_function_ ecb_inline #if ECB_GCC_VERSION(3,1) || ECB_CLANG_VERSION(2,8) #define ecb_attribute(attrlist) __attribute__ (attrlist) #else #define ecb_attribute(attrlist) #endif #if ECB_GCC_VERSION(3,1) || ECB_CLANG_BUILTIN(__builtin_constant_p) #define ecb_is_constant(expr) __builtin_constant_p (expr) #else /* possible C11 impl for integral types typedef struct ecb_is_constant_struct ecb_is_constant_struct; #define ecb_is_constant(expr) _Generic ((1 ? (struct ecb_is_constant_struct *)0 : (void *)((expr) - (expr)), ecb_is_constant_struct *: 0, default: 1)) */ #define ecb_is_constant(expr) 0 #endif #if ECB_GCC_VERSION(3,1) || ECB_CLANG_BUILTIN(__builtin_expect) #define ecb_expect(expr,value) __builtin_expect ((expr),(value)) #else #define ecb_expect(expr,value) (expr) #endif #if ECB_GCC_VERSION(3,1) || ECB_CLANG_BUILTIN(__builtin_prefetch) #define ecb_prefetch(addr,rw,locality) __builtin_prefetch (addr, rw, locality) #else #define ecb_prefetch(addr,rw,locality) #endif /* no emulation for ecb_decltype */ #if ECB_CPP11 // older implementations might have problems with decltype(x)::type, work around it template struct ecb_decltype_t { typedef T type; }; #define ecb_decltype(x) ecb_decltype_t::type #elif ECB_GCC_VERSION(3,0) || ECB_CLANG_VERSION(2,8) #define ecb_decltype(x) __typeof__ (x) #endif #if _MSC_VER >= 1300 #define ecb_deprecated __declspec (deprecated) #else #define ecb_deprecated ecb_attribute ((__deprecated__)) #endif #if _MSC_VER >= 1500 #define ecb_deprecated_message(msg) __declspec (deprecated (msg)) #elif ECB_GCC_VERSION(4,5) #define ecb_deprecated_message(msg) ecb_attribute ((__deprecated__ (msg)) #else #define ecb_deprecated_message(msg) ecb_deprecated #endif #if _MSC_VER >= 1400 #define ecb_noinline __declspec (noinline) #else #define ecb_noinline ecb_attribute ((__noinline__)) #endif #define ecb_unused ecb_attribute ((__unused__)) #define ecb_const ecb_attribute ((__const__)) #define ecb_pure ecb_attribute ((__pure__)) #if ECB_C11 || __IBMC_NORETURN /* http://www-01.ibm.com/support/knowledgecenter/SSGH3R_13.1.0/com.ibm.xlcpp131.aix.doc/language_ref/noreturn.html */ #define ecb_noreturn _Noreturn #elif ECB_CPP11 #define ecb_noreturn [[noreturn]] #elif _MSC_VER >= 1200 /* http://msdn.microsoft.com/en-us/library/k6ktzx3s.aspx */ #define ecb_noreturn __declspec (noreturn) #else #define ecb_noreturn ecb_attribute ((__noreturn__)) #endif #if ECB_GCC_VERSION(4,3) #define ecb_artificial ecb_attribute ((__artificial__)) #define ecb_hot ecb_attribute ((__hot__)) #define ecb_cold ecb_attribute ((__cold__)) #else #define ecb_artificial #define ecb_hot #define ecb_cold #endif /* put around conditional expressions if you are very sure that the */ /* expression is mostly true or mostly false. note that these return */ /* booleans, not the expression. */ #define ecb_expect_false(expr) ecb_expect (!!(expr), 0) #define ecb_expect_true(expr) ecb_expect (!!(expr), 1) /* for compatibility to the rest of the world */ #define ecb_likely(expr) ecb_expect_true (expr) #define ecb_unlikely(expr) ecb_expect_false (expr) /* count trailing zero bits and count # of one bits */ #if ECB_GCC_VERSION(3,4) \ || (ECB_CLANG_BUILTIN(__builtin_clz) && ECB_CLANG_BUILTIN(__builtin_clzll) \ && ECB_CLANG_BUILTIN(__builtin_ctz) && ECB_CLANG_BUILTIN(__builtin_ctzll) \ && ECB_CLANG_BUILTIN(__builtin_popcount)) /* we assume int == 32 bit, long == 32 or 64 bit and long long == 64 bit */ #define ecb_ld32(x) (__builtin_clz (x) ^ 31) #define ecb_ld64(x) (__builtin_clzll (x) ^ 63) #define ecb_ctz32(x) __builtin_ctz (x) #define ecb_ctz64(x) __builtin_ctzll (x) #define ecb_popcount32(x) __builtin_popcount (x) /* no popcountll */ #else ecb_function_ ecb_const int ecb_ctz32 (uint32_t x); ecb_function_ ecb_const int ecb_ctz32 (uint32_t x) { #if 1400 <= _MSC_VER && (_M_IX86 || _M_X64 || _M_IA64 || _M_ARM) unsigned long r; _BitScanForward (&r, x); return (int)r; #else int r = 0; x &= ~x + 1; /* this isolates the lowest bit */ #if ECB_branchless_on_i386 r += !!(x & 0xaaaaaaaa) << 0; r += !!(x & 0xcccccccc) << 1; r += !!(x & 0xf0f0f0f0) << 2; r += !!(x & 0xff00ff00) << 3; r += !!(x & 0xffff0000) << 4; #else if (x & 0xaaaaaaaa) r += 1; if (x & 0xcccccccc) r += 2; if (x & 0xf0f0f0f0) r += 4; if (x & 0xff00ff00) r += 8; if (x & 0xffff0000) r += 16; #endif return r; #endif } ecb_function_ ecb_const int ecb_ctz64 (uint64_t x); ecb_function_ ecb_const int ecb_ctz64 (uint64_t x) { #if 1400 <= _MSC_VER && (_M_X64 || _M_IA64 || _M_ARM) unsigned long r; _BitScanForward64 (&r, x); return (int)r; #else int shift = x & 0xffffffff ? 0 : 32; return ecb_ctz32 (x >> shift) + shift; #endif } ecb_function_ ecb_const int ecb_popcount32 (uint32_t x); ecb_function_ ecb_const int ecb_popcount32 (uint32_t x) { x -= (x >> 1) & 0x55555555; x = ((x >> 2) & 0x33333333) + (x & 0x33333333); x = ((x >> 4) + x) & 0x0f0f0f0f; x *= 0x01010101; return x >> 24; } ecb_function_ ecb_const int ecb_ld32 (uint32_t x); ecb_function_ ecb_const int ecb_ld32 (uint32_t x) { #if 1400 <= _MSC_VER && (_M_IX86 || _M_X64 || _M_IA64 || _M_ARM) unsigned long r; _BitScanReverse (&r, x); return (int)r; #else int r = 0; if (x >> 16) { x >>= 16; r += 16; } if (x >> 8) { x >>= 8; r += 8; } if (x >> 4) { x >>= 4; r += 4; } if (x >> 2) { x >>= 2; r += 2; } if (x >> 1) { r += 1; } return r; #endif } ecb_function_ ecb_const int ecb_ld64 (uint64_t x); ecb_function_ ecb_const int ecb_ld64 (uint64_t x) { #if 1400 <= _MSC_VER && (_M_X64 || _M_IA64 || _M_ARM) unsigned long r; _BitScanReverse64 (&r, x); return (int)r; #else int r = 0; if (x >> 32) { x >>= 32; r += 32; } return r + ecb_ld32 (x); #endif } #endif ecb_function_ ecb_const ecb_bool ecb_is_pot32 (uint32_t x); ecb_function_ ecb_const ecb_bool ecb_is_pot32 (uint32_t x) { return !(x & (x - 1)); } ecb_function_ ecb_const ecb_bool ecb_is_pot64 (uint64_t x); ecb_function_ ecb_const ecb_bool ecb_is_pot64 (uint64_t x) { return !(x & (x - 1)); } ecb_function_ ecb_const uint8_t ecb_bitrev8 (uint8_t x); ecb_function_ ecb_const uint8_t ecb_bitrev8 (uint8_t x) { return ( (x * 0x0802U & 0x22110U) | (x * 0x8020U & 0x88440U)) * 0x10101U >> 16; } ecb_function_ ecb_const uint16_t ecb_bitrev16 (uint16_t x); ecb_function_ ecb_const uint16_t ecb_bitrev16 (uint16_t x) { x = ((x >> 1) & 0x5555) | ((x & 0x5555) << 1); x = ((x >> 2) & 0x3333) | ((x & 0x3333) << 2); x = ((x >> 4) & 0x0f0f) | ((x & 0x0f0f) << 4); x = ( x >> 8 ) | ( x << 8); return x; } ecb_function_ ecb_const uint32_t ecb_bitrev32 (uint32_t x); ecb_function_ ecb_const uint32_t ecb_bitrev32 (uint32_t x) { x = ((x >> 1) & 0x55555555) | ((x & 0x55555555) << 1); x = ((x >> 2) & 0x33333333) | ((x & 0x33333333) << 2); x = ((x >> 4) & 0x0f0f0f0f) | ((x & 0x0f0f0f0f) << 4); x = ((x >> 8) & 0x00ff00ff) | ((x & 0x00ff00ff) << 8); x = ( x >> 16 ) | ( x << 16); return x; } /* popcount64 is only available on 64 bit cpus as gcc builtin */ /* so for this version we are lazy */ ecb_function_ ecb_const int ecb_popcount64 (uint64_t x); ecb_function_ ecb_const int ecb_popcount64 (uint64_t x) { return ecb_popcount32 (x) + ecb_popcount32 (x >> 32); } ecb_inline ecb_const uint8_t ecb_rotl8 (uint8_t x, unsigned int count); ecb_inline ecb_const uint8_t ecb_rotr8 (uint8_t x, unsigned int count); ecb_inline ecb_const uint16_t ecb_rotl16 (uint16_t x, unsigned int count); ecb_inline ecb_const uint16_t ecb_rotr16 (uint16_t x, unsigned int count); ecb_inline ecb_const uint32_t ecb_rotl32 (uint32_t x, unsigned int count); ecb_inline ecb_const uint32_t ecb_rotr32 (uint32_t x, unsigned int count); ecb_inline ecb_const uint64_t ecb_rotl64 (uint64_t x, unsigned int count); ecb_inline ecb_const uint64_t ecb_rotr64 (uint64_t x, unsigned int count); ecb_inline ecb_const uint8_t ecb_rotl8 (uint8_t x, unsigned int count) { return (x >> ( 8 - count)) | (x << count); } ecb_inline ecb_const uint8_t ecb_rotr8 (uint8_t x, unsigned int count) { return (x << ( 8 - count)) | (x >> count); } ecb_inline ecb_const uint16_t ecb_rotl16 (uint16_t x, unsigned int count) { return (x >> (16 - count)) | (x << count); } ecb_inline ecb_const uint16_t ecb_rotr16 (uint16_t x, unsigned int count) { return (x << (16 - count)) | (x >> count); } ecb_inline ecb_const uint32_t ecb_rotl32 (uint32_t x, unsigned int count) { return (x >> (32 - count)) | (x << count); } ecb_inline ecb_const uint32_t ecb_rotr32 (uint32_t x, unsigned int count) { return (x << (32 - count)) | (x >> count); } ecb_inline ecb_const uint64_t ecb_rotl64 (uint64_t x, unsigned int count) { return (x >> (64 - count)) | (x << count); } ecb_inline ecb_const uint64_t ecb_rotr64 (uint64_t x, unsigned int count) { return (x << (64 - count)) | (x >> count); } #if ECB_CPP inline uint8_t ecb_ctz (uint8_t v) { return ecb_ctz32 (v); } inline uint16_t ecb_ctz (uint16_t v) { return ecb_ctz32 (v); } inline uint32_t ecb_ctz (uint32_t v) { return ecb_ctz32 (v); } inline uint64_t ecb_ctz (uint64_t v) { return ecb_ctz64 (v); } inline bool ecb_is_pot (uint8_t v) { return ecb_is_pot32 (v); } inline bool ecb_is_pot (uint16_t v) { return ecb_is_pot32 (v); } inline bool ecb_is_pot (uint32_t v) { return ecb_is_pot32 (v); } inline bool ecb_is_pot (uint64_t v) { return ecb_is_pot64 (v); } inline int ecb_ld (uint8_t v) { return ecb_ld32 (v); } inline int ecb_ld (uint16_t v) { return ecb_ld32 (v); } inline int ecb_ld (uint32_t v) { return ecb_ld32 (v); } inline int ecb_ld (uint64_t v) { return ecb_ld64 (v); } inline int ecb_popcount (uint8_t v) { return ecb_popcount32 (v); } inline int ecb_popcount (uint16_t v) { return ecb_popcount32 (v); } inline int ecb_popcount (uint32_t v) { return ecb_popcount32 (v); } inline int ecb_popcount (uint64_t v) { return ecb_popcount64 (v); } inline uint8_t ecb_bitrev (uint8_t v) { return ecb_bitrev8 (v); } inline uint16_t ecb_bitrev (uint16_t v) { return ecb_bitrev16 (v); } inline uint32_t ecb_bitrev (uint32_t v) { return ecb_bitrev32 (v); } inline uint8_t ecb_rotl (uint8_t v, unsigned int count) { return ecb_rotl8 (v, count); } inline uint16_t ecb_rotl (uint16_t v, unsigned int count) { return ecb_rotl16 (v, count); } inline uint32_t ecb_rotl (uint32_t v, unsigned int count) { return ecb_rotl32 (v, count); } inline uint64_t ecb_rotl (uint64_t v, unsigned int count) { return ecb_rotl64 (v, count); } inline uint8_t ecb_rotr (uint8_t v, unsigned int count) { return ecb_rotr8 (v, count); } inline uint16_t ecb_rotr (uint16_t v, unsigned int count) { return ecb_rotr16 (v, count); } inline uint32_t ecb_rotr (uint32_t v, unsigned int count) { return ecb_rotr32 (v, count); } inline uint64_t ecb_rotr (uint64_t v, unsigned int count) { return ecb_rotr64 (v, count); } #endif #if ECB_GCC_VERSION(4,3) || (ECB_CLANG_BUILTIN(__builtin_bswap32) && ECB_CLANG_BUILTIN(__builtin_bswap64)) #if ECB_GCC_VERSION(4,8) || ECB_CLANG_BUILTIN(__builtin_bswap16) #define ecb_bswap16(x) __builtin_bswap16 (x) #else #define ecb_bswap16(x) (__builtin_bswap32 (x) >> 16) #endif #define ecb_bswap32(x) __builtin_bswap32 (x) #define ecb_bswap64(x) __builtin_bswap64 (x) #elif _MSC_VER #include #define ecb_bswap16(x) ((uint16_t)_byteswap_ushort ((uint16_t)(x))) #define ecb_bswap32(x) ((uint32_t)_byteswap_ulong ((uint32_t)(x))) #define ecb_bswap64(x) ((uint64_t)_byteswap_uint64 ((uint64_t)(x))) #else ecb_function_ ecb_const uint16_t ecb_bswap16 (uint16_t x); ecb_function_ ecb_const uint16_t ecb_bswap16 (uint16_t x) { return ecb_rotl16 (x, 8); } ecb_function_ ecb_const uint32_t ecb_bswap32 (uint32_t x); ecb_function_ ecb_const uint32_t ecb_bswap32 (uint32_t x) { return (((uint32_t)ecb_bswap16 (x)) << 16) | ecb_bswap16 (x >> 16); } ecb_function_ ecb_const uint64_t ecb_bswap64 (uint64_t x); ecb_function_ ecb_const uint64_t ecb_bswap64 (uint64_t x) { return (((uint64_t)ecb_bswap32 (x)) << 32) | ecb_bswap32 (x >> 32); } #endif #if ECB_GCC_VERSION(4,5) || ECB_CLANG_BUILTIN(__builtin_unreachable) #define ecb_unreachable() __builtin_unreachable () #else /* this seems to work fine, but gcc always emits a warning for it :/ */ ecb_inline ecb_noreturn void ecb_unreachable (void); ecb_inline ecb_noreturn void ecb_unreachable (void) { } #endif /* try to tell the compiler that some condition is definitely true */ #define ecb_assume(cond) if (!(cond)) ecb_unreachable (); else 0 ecb_inline ecb_const uint32_t ecb_byteorder_helper (void); ecb_inline ecb_const uint32_t ecb_byteorder_helper (void) { /* the union code still generates code under pressure in gcc, */ /* but less than using pointers, and always seems to */ /* successfully return a constant. */ /* the reason why we have this horrible preprocessor mess */ /* is to avoid it in all cases, at least on common architectures */ /* or when using a recent enough gcc version (>= 4.6) */ #if (defined __BYTE_ORDER__ && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) \ || ((__i386 || __i386__ || _M_IX86 || ECB_GCC_AMD64 || ECB_MSVC_AMD64) && !__VOS__) #define ECB_LITTLE_ENDIAN 1 return 0x44332211; #elif (defined __BYTE_ORDER__ && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) \ || ((__AARCH64EB__ || __MIPSEB__ || __ARMEB__) && !__VOS__) #define ECB_BIG_ENDIAN 1 return 0x11223344; #else union { uint8_t c[4]; uint32_t u; } u = { 0x11, 0x22, 0x33, 0x44 }; return u.u; #endif } ecb_inline ecb_const ecb_bool ecb_big_endian (void); ecb_inline ecb_const ecb_bool ecb_big_endian (void) { return ecb_byteorder_helper () == 0x11223344; } ecb_inline ecb_const ecb_bool ecb_little_endian (void); ecb_inline ecb_const ecb_bool ecb_little_endian (void) { return ecb_byteorder_helper () == 0x44332211; } /*****************************************************************************/ /* unaligned load/store */ ecb_inline uint_fast16_t ecb_be_u16_to_host (uint_fast16_t v) { return ecb_little_endian () ? ecb_bswap16 (v) : v; } ecb_inline uint_fast32_t ecb_be_u32_to_host (uint_fast32_t v) { return ecb_little_endian () ? ecb_bswap32 (v) : v; } ecb_inline uint_fast64_t ecb_be_u64_to_host (uint_fast64_t v) { return ecb_little_endian () ? ecb_bswap64 (v) : v; } ecb_inline uint_fast16_t ecb_le_u16_to_host (uint_fast16_t v) { return ecb_big_endian () ? ecb_bswap16 (v) : v; } ecb_inline uint_fast32_t ecb_le_u32_to_host (uint_fast32_t v) { return ecb_big_endian () ? ecb_bswap32 (v) : v; } ecb_inline uint_fast64_t ecb_le_u64_to_host (uint_fast64_t v) { return ecb_big_endian () ? ecb_bswap64 (v) : v; } ecb_inline uint_fast16_t ecb_peek_u16_u (const void *ptr) { uint16_t v; memcpy (&v, ptr, sizeof (v)); return v; } ecb_inline uint_fast32_t ecb_peek_u32_u (const void *ptr) { uint32_t v; memcpy (&v, ptr, sizeof (v)); return v; } ecb_inline uint_fast64_t ecb_peek_u64_u (const void *ptr) { uint64_t v; memcpy (&v, ptr, sizeof (v)); return v; } ecb_inline uint_fast16_t ecb_peek_be_u16_u (const void *ptr) { return ecb_be_u16_to_host (ecb_peek_u16_u (ptr)); } ecb_inline uint_fast32_t ecb_peek_be_u32_u (const void *ptr) { return ecb_be_u32_to_host (ecb_peek_u32_u (ptr)); } ecb_inline uint_fast64_t ecb_peek_be_u64_u (const void *ptr) { return ecb_be_u64_to_host (ecb_peek_u64_u (ptr)); } ecb_inline uint_fast16_t ecb_peek_le_u16_u (const void *ptr) { return ecb_le_u16_to_host (ecb_peek_u16_u (ptr)); } ecb_inline uint_fast32_t ecb_peek_le_u32_u (const void *ptr) { return ecb_le_u32_to_host (ecb_peek_u32_u (ptr)); } ecb_inline uint_fast64_t ecb_peek_le_u64_u (const void *ptr) { return ecb_le_u64_to_host (ecb_peek_u64_u (ptr)); } ecb_inline uint_fast16_t ecb_host_to_be_u16 (uint_fast16_t v) { return ecb_little_endian () ? ecb_bswap16 (v) : v; } ecb_inline uint_fast32_t ecb_host_to_be_u32 (uint_fast32_t v) { return ecb_little_endian () ? ecb_bswap32 (v) : v; } ecb_inline uint_fast64_t ecb_host_to_be_u64 (uint_fast64_t v) { return ecb_little_endian () ? ecb_bswap64 (v) : v; } ecb_inline uint_fast16_t ecb_host_to_le_u16 (uint_fast16_t v) { return ecb_big_endian () ? ecb_bswap16 (v) : v; } ecb_inline uint_fast32_t ecb_host_to_le_u32 (uint_fast32_t v) { return ecb_big_endian () ? ecb_bswap32 (v) : v; } ecb_inline uint_fast64_t ecb_host_to_le_u64 (uint_fast64_t v) { return ecb_big_endian () ? ecb_bswap64 (v) : v; } ecb_inline void ecb_poke_u16_u (void *ptr, uint16_t v) { memcpy (ptr, &v, sizeof (v)); } ecb_inline void ecb_poke_u32_u (void *ptr, uint32_t v) { memcpy (ptr, &v, sizeof (v)); } ecb_inline void ecb_poke_u64_u (void *ptr, uint64_t v) { memcpy (ptr, &v, sizeof (v)); } ecb_inline void ecb_poke_be_u16_u (void *ptr, uint_fast16_t v) { ecb_poke_u16_u (ptr, ecb_host_to_be_u16 (v)); } ecb_inline void ecb_poke_be_u32_u (void *ptr, uint_fast32_t v) { ecb_poke_u32_u (ptr, ecb_host_to_be_u32 (v)); } ecb_inline void ecb_poke_be_u64_u (void *ptr, uint_fast64_t v) { ecb_poke_u64_u (ptr, ecb_host_to_be_u64 (v)); } ecb_inline void ecb_poke_le_u16_u (void *ptr, uint_fast16_t v) { ecb_poke_u16_u (ptr, ecb_host_to_le_u16 (v)); } ecb_inline void ecb_poke_le_u32_u (void *ptr, uint_fast32_t v) { ecb_poke_u32_u (ptr, ecb_host_to_le_u32 (v)); } ecb_inline void ecb_poke_le_u64_u (void *ptr, uint_fast64_t v) { ecb_poke_u64_u (ptr, ecb_host_to_le_u64 (v)); } #if ECB_CPP inline uint8_t ecb_bswap (uint8_t v) { return v; } inline uint16_t ecb_bswap (uint16_t v) { return ecb_bswap16 (v); } inline uint32_t ecb_bswap (uint32_t v) { return ecb_bswap32 (v); } inline uint64_t ecb_bswap (uint64_t v) { return ecb_bswap64 (v); } template inline T ecb_be_to_host (T v) { return ecb_little_endian () ? ecb_bswap (v) : v; } template inline T ecb_le_to_host (T v) { return ecb_big_endian () ? ecb_bswap (v) : v; } template inline T ecb_peek (const void *ptr) { return *(const T *)ptr; } template inline T ecb_peek_be (const void *ptr) { return ecb_be_to_host (ecb_peek (ptr)); } template inline T ecb_peek_le (const void *ptr) { return ecb_le_to_host (ecb_peek (ptr)); } template inline T ecb_peek_u (const void *ptr) { T v; memcpy (&v, ptr, sizeof (v)); return v; } template inline T ecb_peek_be_u (const void *ptr) { return ecb_be_to_host (ecb_peek_u (ptr)); } template inline T ecb_peek_le_u (const void *ptr) { return ecb_le_to_host (ecb_peek_u (ptr)); } template inline T ecb_host_to_be (T v) { return ecb_little_endian () ? ecb_bswap (v) : v; } template inline T ecb_host_to_le (T v) { return ecb_big_endian () ? ecb_bswap (v) : v; } template inline void ecb_poke (void *ptr, T v) { *(T *)ptr = v; } template inline void ecb_poke_be (void *ptr, T v) { return ecb_poke (ptr, ecb_host_to_be (v)); } template inline void ecb_poke_le (void *ptr, T v) { return ecb_poke (ptr, ecb_host_to_le (v)); } template inline void ecb_poke_u (void *ptr, T v) { memcpy (ptr, &v, sizeof (v)); } template inline void ecb_poke_be_u (void *ptr, T v) { return ecb_poke_u (ptr, ecb_host_to_be (v)); } template inline void ecb_poke_le_u (void *ptr, T v) { return ecb_poke_u (ptr, ecb_host_to_le (v)); } #endif /*****************************************************************************/ /* division */ #if ECB_GCC_VERSION(3,0) || ECB_C99 /* C99 tightened the definition of %, so we can use a more efficient version */ #define ecb_mod(m,n) ((m) % (n) + ((m) % (n) < 0 ? (n) : 0)) #else #define ecb_mod(m,n) ((m) < 0 ? ((n) - 1 - ((-1 - (m)) % (n))) : ((m) % (n))) #endif #if ECB_CPP template static inline T ecb_div_rd (T val, T div) { return val < 0 ? - ((-val + div - 1) / div) : (val ) / div; } template static inline T ecb_div_ru (T val, T div) { return val < 0 ? - ((-val ) / div) : (val + div - 1) / div; } #else #define ecb_div_rd(val,div) ((val) < 0 ? - ((-(val) + (div) - 1) / (div)) : ((val) ) / (div)) #define ecb_div_ru(val,div) ((val) < 0 ? - ((-(val) ) / (div)) : ((val) + (div) - 1) / (div)) #endif /*****************************************************************************/ /* array length */ #if ecb_cplusplus_does_not_suck /* does not work for local types (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2657.htm) */ template static inline int ecb_array_length (const T (&arr)[N]) { return N; } #else #define ecb_array_length(name) (sizeof (name) / sizeof (name [0])) #endif /*****************************************************************************/ /* IEEE 754-2008 half float conversions */ ecb_function_ ecb_const uint32_t ecb_binary16_to_binary32 (uint32_t x); ecb_function_ ecb_const uint32_t ecb_binary16_to_binary32 (uint32_t x) { unsigned int s = (x & 0x8000) << (31 - 15); int e = (x >> 10) & 0x001f; unsigned int m = x & 0x03ff; if (ecb_expect_false (e == 31)) /* infinity or NaN */ e = 255 - (127 - 15); else if (ecb_expect_false (!e)) { if (ecb_expect_true (!m)) /* zero, handled by code below by forcing e to 0 */ e = 0 - (127 - 15); else { /* subnormal, renormalise */ unsigned int s = 10 - ecb_ld32 (m); m = (m << s) & 0x3ff; /* mask implicit bit */ e -= s - 1; } } /* e and m now are normalised, or zero, (or inf or nan) */ e += 127 - 15; return s | (e << 23) | (m << (23 - 10)); } ecb_function_ ecb_const uint16_t ecb_binary32_to_binary16 (uint32_t x); ecb_function_ ecb_const uint16_t ecb_binary32_to_binary16 (uint32_t x) { unsigned int s = (x >> 16) & 0x00008000; /* sign bit, the easy part */ int e = ((x >> 23) & 0x000000ff) - (127 - 15); /* the desired exponent */ unsigned int m = x & 0x007fffff; x &= 0x7fffffff; /* if it's within range of binary16 normals, use fast path */ if (ecb_expect_true (0x38800000 <= x && x <= 0x477fefff)) { /* mantissa round-to-even */ m += 0x00000fff + ((m >> (23 - 10)) & 1); /* handle overflow */ if (ecb_expect_false (m >= 0x00800000)) { m >>= 1; e += 1; } return s | (e << 10) | (m >> (23 - 10)); } /* handle large numbers and infinity */ if (ecb_expect_true (0x477fefff < x && x <= 0x7f800000)) return s | 0x7c00; /* handle zero, subnormals and small numbers */ if (ecb_expect_true (x < 0x38800000)) { /* zero */ if (ecb_expect_true (!x)) return s; /* handle subnormals */ /* too small, will be zero */ if (e < (14 - 24)) /* might not be sharp, but is good enough */ return s; m |= 0x00800000; /* make implicit bit explicit */ /* very tricky - we need to round to the nearest e (+10) bit value */ { unsigned int bits = 14 - e; unsigned int half = (1 << (bits - 1)) - 1; unsigned int even = (m >> bits) & 1; /* if this overflows, we will end up with a normalised number */ m = (m + half + even) >> bits; } return s | m; } /* handle NaNs, preserve leftmost nan bits, but make sure we don't turn them into infinities */ m >>= 13; return s | 0x7c00 | m | !m; } /*******************************************************************************/ /* fast integer to ascii */ /* * This code is pretty complicated because it is general. The idea behind it, * however, is pretty simple: first, the number is multiplied with a scaling * factor (2**bits / 10**(digits-1)) to convert the integer into a fixed-point * number with the first digit in the upper bits. * Then this digit is converted to text and masked out. The resulting number * is then multiplied by 10, by multiplying the fixed point representation * by 5 and shifting the (binary) decimal point one to the right, so a 4.28 * format becomes 5.27, 6.26 and so on. * The rest involves only advancing the pointer if we already generated a * non-zero digit, so leading zeroes are overwritten. */ // simply return a mask with "bits" bits set #define ecb_i2a_mask(type,bits) ((((type)1) << (bits)) - 1) // oputput a single digit. maskvalue is 10**digitidx #define ecb_i2a_digit(type,bits,digitmask,maskvalue,digitidx) \ if (digitmask >= maskvalue) /* constant, used to decide how many digits to generate */ \ { \ char digit = x >> (bits - digitidx); /* calculate the topmost digit */ \ *ptr = digit + '0'; /* output it */ \ nz = (digitmask == maskvalue) || nz || digit; /* first term == always output last digit */ \ ptr += nz; /* output digit only if non-zero digit seen */ \ x = (x & ecb_i2a_mask (type, bits - digitidx)) * 5; /* *10, but shift decimal point right */ \ } // convert integer to fixed point format and multiply out digits, highest first // requires magic constants: max. digits and number of bits after the decimal point #define ecb_i2a_def(suffix,ptr,v,type,bits,digitmask,lz) \ ecb_inline char *ecb_i2a_ ## suffix (char *ptr, uint32_t u) \ { \ char nz = lz; /* non-zero digit seen? */ \ /* convert to x.bits fixed-point */ \ type x = u * ((ecb_i2a_mask (type, bits) + digitmask) / digitmask); \ /* output up to 10 digits */ \ ecb_i2a_digit (type,bits,digitmask, 1, 0); \ ecb_i2a_digit (type,bits,digitmask, 10, 1); \ ecb_i2a_digit (type,bits,digitmask, 100, 2); \ ecb_i2a_digit (type,bits,digitmask, 1000, 3); \ ecb_i2a_digit (type,bits,digitmask, 10000, 4); \ ecb_i2a_digit (type,bits,digitmask, 100000, 5); \ ecb_i2a_digit (type,bits,digitmask, 1000000, 6); \ ecb_i2a_digit (type,bits,digitmask, 10000000, 7); \ ecb_i2a_digit (type,bits,digitmask, 100000000, 8); \ ecb_i2a_digit (type,bits,digitmask, 1000000000, 9); \ return ptr; \ } // predefined versions of the above, for various digits // ecb_i2a_xN = almost N digits, limit defined by macro // ecb_i2a_N = up to N digits, leading zeroes suppressed // ecb_i2a_0N = exactly N digits, including leading zeroes // non-leading-zero versions, limited range #define ECB_I2A_MAX_X5 59074 // limit for ecb_i2a_x5 #define ECB_I2A_MAX_X10 2932500665 // limit for ecb_i2a_x10 ecb_i2a_def ( x5, ptr, v, uint32_t, 26, 10000, 0) ecb_i2a_def (x10, ptr, v, uint64_t, 60, 1000000000, 0) // non-leading zero versions, all digits, 4 and 9 are optimal for 32/64 bit ecb_i2a_def ( 2, ptr, v, uint32_t, 10, 10, 0) ecb_i2a_def ( 3, ptr, v, uint32_t, 12, 100, 0) ecb_i2a_def ( 4, ptr, v, uint32_t, 26, 1000, 0) ecb_i2a_def ( 5, ptr, v, uint64_t, 30, 10000, 0) ecb_i2a_def ( 6, ptr, v, uint64_t, 36, 100000, 0) ecb_i2a_def ( 7, ptr, v, uint64_t, 44, 1000000, 0) ecb_i2a_def ( 8, ptr, v, uint64_t, 50, 10000000, 0) ecb_i2a_def ( 9, ptr, v, uint64_t, 56, 100000000, 0) // leading-zero versions, all digits, 04 and 09 are optimal for 32/64 bit ecb_i2a_def (02, ptr, v, uint32_t, 10, 10, 1) ecb_i2a_def (03, ptr, v, uint32_t, 12, 100, 1) ecb_i2a_def (04, ptr, v, uint32_t, 26, 1000, 1) ecb_i2a_def (05, ptr, v, uint64_t, 30, 10000, 1) ecb_i2a_def (06, ptr, v, uint64_t, 36, 100000, 1) ecb_i2a_def (07, ptr, v, uint64_t, 44, 1000000, 1) ecb_i2a_def (08, ptr, v, uint64_t, 50, 10000000, 1) ecb_i2a_def (09, ptr, v, uint64_t, 56, 100000000, 1) #define ECB_I2A_I32_DIGITS 11 #define ECB_I2A_U32_DIGITS 10 #define ECB_I2A_I64_DIGITS 20 #define ECB_I2A_U64_DIGITS 21 #define ECB_I2A_MAX_DIGITS 21 ecb_inline char * ecb_i2a_u32 (char *ptr, uint32_t u) { #if ECB_64BIT_NATIVE if (ecb_expect_true (u <= ECB_I2A_MAX_X10)) ptr = ecb_i2a_x10 (ptr, u); else // x10 almost, but not fully, covers 32 bit { uint32_t u1 = u % 1000000000; uint32_t u2 = u / 1000000000; *ptr++ = u2 + '0'; ptr = ecb_i2a_09 (ptr, u1); } #else if (ecb_expect_true (u <= ECB_I2A_MAX_X5)) ecb_i2a_x5 (ptr, u); else if (ecb_expect_true (u <= ECB_I2A_MAX_X5 * 10000)) { uint32_t u1 = u % 10000; uint32_t u2 = u / 10000; ptr = ecb_i2a_x5 (ptr, u2); ptr = ecb_i2a_04 (ptr, u1); } else { uint32_t u1 = u % 10000; uint32_t ua = u / 10000; uint32_t u2 = ua % 10000; uint32_t u3 = ua / 10000; ptr = ecb_i2a_2 (ptr, u3); ptr = ecb_i2a_04 (ptr, u2); ptr = ecb_i2a_04 (ptr, u1); } #endif return ptr; } ecb_inline char * ecb_i2a_i32 (char *ptr, int32_t v) { *ptr = '-'; ptr += v < 0; uint32_t u = v < 0 ? -(uint32_t)v : v; #if ECB_64BIT_NATIVE ptr = ecb_i2a_x10 (ptr, u); // x10 fully covers 31 bit #else ptr = ecb_i2a_u32 (ptr, u); #endif return ptr; } ecb_inline char * ecb_i2a_u64 (char *ptr, uint64_t u) { #if ECB_64BIT_NATIVE if (ecb_expect_true (u <= ECB_I2A_MAX_X10)) ptr = ecb_i2a_x10 (ptr, u); else if (ecb_expect_false (u <= ECB_I2A_MAX_X10 * 1000000000)) { uint64_t u1 = u % 1000000000; uint64_t u2 = u / 1000000000; ptr = ecb_i2a_x10 (ptr, u2); ptr = ecb_i2a_09 (ptr, u1); } else { uint64_t u1 = u % 1000000000; uint64_t ua = u / 1000000000; uint64_t u2 = ua % 1000000000; uint64_t u3 = ua / 1000000000; ptr = ecb_i2a_2 (ptr, u3); ptr = ecb_i2a_09 (ptr, u2); ptr = ecb_i2a_09 (ptr, u1); } #else if (ecb_expect_true (u <= ECB_I2A_MAX_X5)) ptr = ecb_i2a_x5 (ptr, u); else { uint64_t u1 = u % 10000; uint64_t u2 = u / 10000; ptr = ecb_i2a_u64 (ptr, u2); ptr = ecb_i2a_04 (ptr, u1); } #endif return ptr; } ecb_inline char * ecb_i2a_i64 (char *ptr, int64_t v) { *ptr = '-'; ptr += v < 0; uint64_t u = v < 0 ? -(uint64_t)v : v; #if ECB_64BIT_NATIVE if (ecb_expect_true (u <= ECB_I2A_MAX_X10)) ptr = ecb_i2a_x10 (ptr, u); else if (ecb_expect_false (u <= ECB_I2A_MAX_X10 * 1000000000)) { uint64_t u1 = u % 1000000000; uint64_t u2 = u / 1000000000; ptr = ecb_i2a_x10 (ptr, u2); ptr = ecb_i2a_09 (ptr, u1); } else { uint64_t u1 = u % 1000000000; uint64_t ua = u / 1000000000; uint64_t u2 = ua % 1000000000; uint64_t u3 = ua / 1000000000; // 2**31 is 19 digits, so the top is exactly one digit *ptr++ = u3 + '0'; ptr = ecb_i2a_09 (ptr, u2); ptr = ecb_i2a_09 (ptr, u1); } #else ptr = ecb_i2a_u64 (ptr, u); #endif return ptr; } /*******************************************************************************/ /* floating point stuff, can be disabled by defining ECB_NO_LIBM */ /* basically, everything uses "ieee pure-endian" floating point numbers */ /* the only noteworthy exception is ancient armle, which uses order 43218765 */ #if 0 \ || __i386 || __i386__ \ || ECB_GCC_AMD64 \ || __powerpc__ || __ppc__ || __powerpc64__ || __ppc64__ \ || defined __s390__ || defined __s390x__ \ || defined __mips__ \ || defined __alpha__ \ || defined __hppa__ \ || defined __ia64__ \ || defined __m68k__ \ || defined __m88k__ \ || defined __sh__ \ || defined _M_IX86 || defined ECB_MSVC_AMD64 || defined _M_IA64 \ || (defined __arm__ && (defined __ARM_EABI__ || defined __EABI__ || defined __VFP_FP__ || defined _WIN32_WCE || defined __ANDROID__)) \ || defined __aarch64__ #define ECB_STDFP 1 #else #define ECB_STDFP 0 #endif #ifndef ECB_NO_LIBM #include /* for frexp*, ldexp*, INFINITY, NAN */ /* only the oldest of old doesn't have this one. solaris. */ #ifdef INFINITY #define ECB_INFINITY INFINITY #else #define ECB_INFINITY HUGE_VAL #endif #ifdef NAN #define ECB_NAN NAN #else #define ECB_NAN ECB_INFINITY #endif #if ECB_C99 || _XOPEN_VERSION >= 600 || _POSIX_VERSION >= 200112L #define ecb_ldexpf(x,e) ldexpf ((x), (e)) #define ecb_frexpf(x,e) frexpf ((x), (e)) #else #define ecb_ldexpf(x,e) (float) ldexp ((double) (x), (e)) #define ecb_frexpf(x,e) (float) frexp ((double) (x), (e)) #endif /* convert a float to ieee single/binary32 */ ecb_function_ ecb_const uint32_t ecb_float_to_binary32 (float x); ecb_function_ ecb_const uint32_t ecb_float_to_binary32 (float x) { uint32_t r; #if ECB_STDFP memcpy (&r, &x, 4); #else /* slow emulation, works for anything but -0 */ uint32_t m; int e; if (x == 0e0f ) return 0x00000000U; if (x > +3.40282346638528860e+38f) return 0x7f800000U; if (x < -3.40282346638528860e+38f) return 0xff800000U; if (x != x ) return 0x7fbfffffU; m = ecb_frexpf (x, &e) * 0x1000000U; r = m & 0x80000000U; if (r) m = -m; if (e <= -126) { m &= 0xffffffU; m >>= (-125 - e); e = -126; } r |= (e + 126) << 23; r |= m & 0x7fffffU; #endif return r; } /* converts an ieee single/binary32 to a float */ ecb_function_ ecb_const float ecb_binary32_to_float (uint32_t x); ecb_function_ ecb_const float ecb_binary32_to_float (uint32_t x) { float r; #if ECB_STDFP memcpy (&r, &x, 4); #else /* emulation, only works for normals and subnormals and +0 */ int neg = x >> 31; int e = (x >> 23) & 0xffU; x &= 0x7fffffU; if (e) x |= 0x800000U; else e = 1; /* we distrust ldexpf a bit and do the 2**-24 scaling by an extra multiply */ r = ecb_ldexpf (x * (0.5f / 0x800000U), e - 126); r = neg ? -r : r; #endif return r; } /* convert a double to ieee double/binary64 */ ecb_function_ ecb_const uint64_t ecb_double_to_binary64 (double x); ecb_function_ ecb_const uint64_t ecb_double_to_binary64 (double x) { uint64_t r; #if ECB_STDFP memcpy (&r, &x, 8); #else /* slow emulation, works for anything but -0 */ uint64_t m; int e; if (x == 0e0 ) return 0x0000000000000000U; if (x > +1.79769313486231470e+308) return 0x7ff0000000000000U; if (x < -1.79769313486231470e+308) return 0xfff0000000000000U; if (x != x ) return 0X7ff7ffffffffffffU; m = frexp (x, &e) * 0x20000000000000U; r = m & 0x8000000000000000;; if (r) m = -m; if (e <= -1022) { m &= 0x1fffffffffffffU; m >>= (-1021 - e); e = -1022; } r |= ((uint64_t)(e + 1022)) << 52; r |= m & 0xfffffffffffffU; #endif return r; } /* converts an ieee double/binary64 to a double */ ecb_function_ ecb_const double ecb_binary64_to_double (uint64_t x); ecb_function_ ecb_const double ecb_binary64_to_double (uint64_t x) { double r; #if ECB_STDFP memcpy (&r, &x, 8); #else /* emulation, only works for normals and subnormals and +0 */ int neg = x >> 63; int e = (x >> 52) & 0x7ffU; x &= 0xfffffffffffffU; if (e) x |= 0x10000000000000U; else e = 1; /* we distrust ldexp a bit and do the 2**-53 scaling by an extra multiply */ r = ldexp (x * (0.5 / 0x10000000000000U), e - 1022); r = neg ? -r : r; #endif return r; } /* convert a float to ieee half/binary16 */ ecb_function_ ecb_const uint16_t ecb_float_to_binary16 (float x); ecb_function_ ecb_const uint16_t ecb_float_to_binary16 (float x) { return ecb_binary32_to_binary16 (ecb_float_to_binary32 (x)); } /* convert an ieee half/binary16 to float */ ecb_function_ ecb_const float ecb_binary16_to_float (uint16_t x); ecb_function_ ecb_const float ecb_binary16_to_float (uint16_t x) { return ecb_binary32_to_float (ecb_binary16_to_binary32 (x)); } #endif #endif libptytty-2.0/src/ptytty.h0000644000175000017500000000126214102517460015225 0ustar giaqemgiaqem#ifndef PTYTTY_H #define PTYTTY_H #include "libptytty.h" #include "ptytty_conf.h" #include #if UTMP_SUPPORT # if defined(HAVE_STRUCT_UTMPX) && !defined(__GLIBC__) # define USE_UTMPX # elif defined(HAVE_STRUCT_UTMP) # define USE_UTMP # else # error cannot build with utmp support - no utmp or utmpx struct found # endif #endif struct ptytty_unix : ptytty { private: char *name; #if UTMP_SUPPORT int utmp_pos; int cmd_pid; bool login_shell; #endif void log_session (bool login, const char *hostname); void put (); public: ptytty_unix (); ~ptytty_unix (); bool get (); void login (int cmd_pid, bool login_shell, const char *hostname); }; #endif libptytty-2.0/src/proxy.C0000644000175000017500000001624114076743321014776 0ustar giaqemgiaqem/*----------------------------------------------------------------------* * File: proxy.C *----------------------------------------------------------------------* * * All portions of code are copyright by their respective author/s. * Copyright (c) 2006 Marc Lehmann * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. *---------------------------------------------------------------------*/ #include "config.h" #include "ptytty.h" #include "estl.h" #include #include #include #include #include #include #include #include // helper/proxy support #if PTYTTY_HELPER static int sock_fd = -1, lock_fd = -1; static int helper_pid; struct command { enum { get, login, destroy } type; ptytty *id; bool login_shell; int cmd_pid; char hostname[512]; // arbitrary, but should be plenty }; struct ptytty_proxy : ptytty { private: ptytty *id; public: ptytty_proxy () : id(0) { } ~ptytty_proxy (); bool get (); void login (int cmd_pid, bool login_shell, const char *hostname); }; #if PTYTTY_REENTRANT # define NEED_TOKEN do { char ch; read (lock_fd, &ch , 1); } while (0) # define GIVE_TOKEN write (lock_fd, &lock_fd, 1) #else # define NEED_TOKEN (void)0 # define GIVE_TOKEN (void)0 #endif bool ptytty_proxy::get () { NEED_TOKEN; command cmd; cmd.type = command::get; write (sock_fd, &cmd, sizeof (cmd)); if (read (sock_fd, &id, sizeof (id)) != sizeof (id)) throw ptytty_error ("protocol error while creating pty using helper process.\n"); if (!id) { GIVE_TOKEN; return false; } if ((pty = recv_fd (sock_fd)) < 0 || (tty = recv_fd (sock_fd)) < 0) throw ptytty_error ("protocol error while reading pty/tty fds from helper process.\n"); GIVE_TOKEN; return true; } void ptytty_proxy::login (int cmd_pid, bool login_shell, const char *hostname) { NEED_TOKEN; command cmd; cmd.type = command::login; cmd.id = id; cmd.cmd_pid = cmd_pid; cmd.login_shell = login_shell; strncpy (cmd.hostname, hostname, sizeof (cmd.hostname)); write (sock_fd, &cmd, sizeof (cmd)); GIVE_TOKEN; } ptytty_proxy::~ptytty_proxy () { if (id) { close_tty (); if (pty >= 0) close (pty); NEED_TOKEN; command cmd; cmd.type = command::destroy; cmd.id = id; write (sock_fd, &cmd, sizeof (cmd)); GIVE_TOKEN; } } static void serve () { command cmd; vector ptys; for (;;) { GIVE_TOKEN; if (read (sock_fd, &cmd, sizeof (command)) != sizeof (command)) break; if (cmd.type == command::get) { // -> id ptyfd ttyfd cmd.id = new ptytty_unix; if (cmd.id->get ()) { write (sock_fd, &cmd.id, sizeof (cmd.id)); ptys.push_back (cmd.id); ptytty::send_fd (sock_fd, cmd.id->pty); ptytty::send_fd (sock_fd, cmd.id->tty); cmd.id->close_tty (); } else { delete cmd.id; cmd.id = 0; write (sock_fd, &cmd.id, sizeof (cmd.id)); } } else if (cmd.type == command::login) { #if UTMP_SUPPORT if (find (ptys.begin (), ptys.end (), cmd.id) != ptys.end ()) { cmd.hostname[sizeof (cmd.hostname) - 1] = 0; cmd.id->login (cmd.cmd_pid, cmd.login_shell, cmd.hostname); } #endif } else if (cmd.type == command::destroy) { vector::iterator pty = find (ptys.begin (), ptys.end (), cmd.id); if (pty != ptys.end ()) { delete *pty; ptys.erase (pty); } } else break; NEED_TOKEN; } // destroy all ptys for (vector::iterator i = ptys.end (); i-- > ptys.begin (); ) delete *i; } void ptytty::use_helper () { if (sock_fd >= 0) return; int sv[2]; if (socketpair (AF_UNIX, SOCK_STREAM, 0, sv)) throw ptytty_error ("could not create socket to communicate with pty/sessiondb helper.\n"); #if PTYTTY_REENTRANT int lv[2]; if (socketpair (AF_UNIX, SOCK_STREAM, 0, lv)) throw ptytty_error ("could not create socket to communicate with pty/sessiondb helper.\n"); #endif helper_pid = fork (); if (helper_pid < 0) throw ptytty_error ("could not create pty/sessiondb helper process.\n"); if (helper_pid) { // client, process sock_fd = sv[0]; close (sv[1]); fcntl (sock_fd, F_SETFD, FD_CLOEXEC); #if PTYTTY_REENTRANT lock_fd = lv[0]; close (lv[1]); fcntl (lock_fd, F_SETFD, FD_CLOEXEC); #endif } else { // server, pty-helper sock_fd = sv[1]; #if PTYTTY_REENTRANT lock_fd = lv[1]; #endif chdir ("/"); signal (SIGHUP, SIG_IGN); signal (SIGTERM, SIG_IGN); signal (SIGINT, SIG_IGN); signal (SIGPIPE, SIG_IGN); for (int fd = 0; fd < 1023; fd++) if (fd != sock_fd && fd != lock_fd) close (fd); serve (); _exit (EXIT_SUCCESS); } } #endif ptytty * ptytty::create () { #if PTYTTY_HELPER if (helper_pid) // use helper process return new ptytty_proxy; else #endif return new ptytty_unix; } void ptytty::sanitise_stdfd () { // sanitise stdin/stdout/stderr to point to *something*. for (int fd = 0; fd <= 2; ++fd) if (fcntl (fd, F_GETFL) < 0 && errno == EBADF) { int fd2 = open ("/dev/tty", fd ? O_WRONLY : O_RDONLY); if (fd2 < 0) fd2 = open ("/dev/null", fd ? O_WRONLY : O_RDONLY); if (fd2 != fd) throw ptytty_error ("unable to sanitise fds.\n"); } } void ptytty::init () { sanitise_stdfd (); uid_t uid = getuid (); gid_t gid = getgid (); // before doing anything else, check for setuid/setgid operation, // start the helper process and drop privileges if (uid != geteuid () || gid != getegid ()) { #if PTYTTY_HELPER use_helper (); #endif drop_privileges (); } } void ptytty::drop_privileges () { uid_t uid = getuid (); gid_t gid = getgid (); // drop privileges #if HAVE_SETRESUID setresgid (gid, gid, gid); setresuid (uid, uid, uid); #elif HAVE_SETREUID setregid (gid, gid); setreuid (uid, uid); #elif HAVE_SETUID setgid (gid); setuid (uid); #else # error no way to drop privileges, configure failed? #endif if (uid != geteuid () || gid != getegid ()) throw ptytty_error ("unable to drop privileges.\n"); } libptytty-2.0/libptytty.pc.in0000644000175000017500000000031014071013267015677 0ustar giaqemgiaqemprefix=@prefix@ Name: libptytty Description: OS independent and secure pty/tty and utmp/wtmp/lastlog handling Version: @VERSION@ Cflags: -I@includedir@ Libs: -L@libdir@ -lptytty Libs.private: @LIBS@ libptytty-2.0/README0000644000175000017500000002270214102517460013572 0ustar giaqemgiaqemNAME libptytty - OS independent and secure pty/tty and utmp/wtmp/lastlog handling SYNOPSIS cc ... -lptytty #include // C++ ptytty *pty = ptytty::create (); if (!pty->get ()) // error allocating pty if (we want utmp) pty->login (process_pid, 0, "remote.host"); else if (we want utmp AND wtmp/lastlog) pty->login (process_pid, 1, "remote.host"); // we are done with it delete pty; // C PTYTTY pty = ptytty_create (); if (!ptytty_get (pty)) // error allocating pty if (we want utmp) ptytty_login (pty, process_pid, 0, "remote.host"); else if (we want utmp AND wtmp/lastlog) ptytty_login (pty, process_pid, 1, "remote.host"); // we are done with it ptytty_delete (pty); See also the eg/ directory, which currently contains the c-sample.c file that spawns a login shell from C using libptytty. DESCRIPTION Libptytty is a small library that offers pseudo-tty management in an OS-independent way. It was created out of frustration over the many differences of pty/tty handling in different operating systems for the use inside "rxvt-unicode". In addition to offering mere pty/tty management, it also offers session database support (utmp and optional wtmp/lastlog updates for login shells). It also supports fork'ing after startup and dropping privileges in the calling process, so in case the calling process gets compromised by the user starting the program there is less to gain, as only the helper process runs with privileges (e.g. setuid/setgid), which reduces the area of attack immensely. Libptytty is written in C++, but it also offers a C-only API. INSTALLATION libptytty uses "CMake" as build system. To build libptytty, install "CMake" and run the following commands from either the libptytty source directory or a separate build directory: cmake -DCMAKE_INSTALL_PREFIX= -DBUILD_SHARED_LIBS=ON cmake --build . cmake --install . SECURITY CONSIDERATIONS *It is of paramount importance that you at least read the following paragraph!* If you write a typical terminal-like program that just wants one or more ptys, you should call the "ptytty::init ()" method (C: "ptytty_init ()" function) as the very first thing in your program: int main (int argc, char *argv[]) { // do nothing here ptytty::init (); // in C: ptytty_init (); // initialise, parse arguments, etc. } This checks whether the program runs setuid or setgid. If yes then it will fork a helper process and drop privileges. Some programs need finer control over if and when this helper process is started, and if and how to drop privileges. For those programs, the methods "ptytty::use_helper" and "ptytty::drop_privileges" (and possibly "ptytty::sanitise_stdfd") are more useful. C++ INTERFACE: THE ptytty CLASS STATIC METHODS ptytty::init () The default way to initialise libptytty. Must be called immediately as the first thing in the "main" function, or earlier e.g. during static construction time. The earlier, the better. This method calls "sanitise_stdfd" and then checks whether the program runs with setuid/setgid permissions and, if yes, spawns a helper process for pty/tty management. It then drops the privileges completely, so the actual program runs without setuid/setgid privileges. On failure, this method throws a "ptytty_error" exception. ptytty::use_helper () Tries to start a helper process that retains privileges even when the calling process does not. This is usually called from "ptytty::init" when it detects that the program is running setuid or setgid, but can be called manually if it is inconvenient to drop privileges at startup, or when you are not running setuid/setgid but want to drop privileges (e.g. when running as a root-started daemon). This method will try not to start more than one helper process. The same helper process can usually be used both from the process starting it and all its fork'ed (not exec'ed) children. On failure, this method throws a "ptytty_error" exception. ptytty::drop_privileges () Drops privileges completely, i.e. sets real, effective and saved user id to the real user id. Useful to make sure that the process doesn't run with special privileges. On failure, this method throws a "ptytty_error" exception. ptytty::sanitise_stdfd () Checks whether file descriptors 0, 1 and 2 (stdin, stdout and stderr) are valid (open) and, if not, connects them to /dev/tty or /dev/null if possible. This is necessary because libptytty might want to output error messages to those descriptors, which at the time of outputting the error message, might be connected to something unsuitable opened by the unsuspecting program itself (this can be a security issue). On failure, this method throws a "ptytty_error" exception. bool success = ptytty::send_fd (int socket, int fd) Utility method to send a file descriptor over a unix domain socket. Returns true if successful, false otherwise. This method is only exposed for your convenience and is not required for normal operation. int fd = ptytty::recv_fd (int socket) Utility method to receive a file descriptor over a unix domain socket. Returns the fd if successful and -1 otherwise. This method is only exposed for your convenience and is not required for normal operation. ptytty *pty = ptytty::create () Creates new ptytty object. Creation does not yet do anything besides allocating the structure. A static method is used because the actual ptytty implementation can differ at runtime, so you need a dynamic object creation facility. DYNAMIC/SESSION-RELATED DATA MEMBERS AND METHODS int pty_fd = pty->pty int tty_fd = pty->tty These members contain the pty and tty file descriptors, respectively. They initially contain -1 until a successful call to "ptytty::get". bool success = pty->get () Tries to find, allocate and initialise a new pty/tty pair. Returns "true" when successful. If the helper process is running and there is a protocol error, this method throws a "ptytty_error" exception. pty->login (int cmd_pid, bool login_shell, const char *hostname) Creates an entry in the systems session database(s) (utmp, wtmp, lastlog). "cmd_pid" must be the pid of the process representing the session (such as the login shell), "login_shell" defines whether the session is associated with a login, which influences whether wtmp and lastlog entries are created, and "hostname" should identify the "hostname" the user logs in from, which often is the value of the "DISPLAY" variable or tty line in case of local logins. Calling this method is optional. A session starts at the time of the login call and extends until the ptytty object is destroyed. pty->close_tty () Closes the tty. Useful after forking in the parent/pty process. bool success = pty->make_controlling_tty () Tries to make the pty/tty pair the controlling terminal of the current process. Useful after forking in the child/tty process. pty->set_utf8_mode (bool on) On systems supporting special UTF-8 line disciplines (e.g. Linux), this tries to enable this discipline for the given pty. Can be called at any time to change the mode. C INTERFACE: THE ptytty FAMILY OF FUNCTIONS ptytty_init () See "ptytty::init ()". PTYTTY ptytty_create () Creates a new opaque PTYTTY object and returns it. Do not try to access it in any way except by testing it for truthness (e.g. "if (pty) ...."). See "ptytty::create ()". int ptytty_pty (PTYTTY ptytty) Return the pty file descriptor. See "pty->pty". int ptytty_tty (PTYTTY ptytty) Return the tty file descriptor. See "pty->tty". void ptytty_delete (PTYTTY ptytty) Destroys the PTYTTY object, freeing the pty/tty pair and cleaning up the utmp/wtmp/lastlog databases, if initialised/used. Same as "delete pty" in C++. int ptytty_get (PTYTTY ptytty) See "pty->get", returns 0 in case of an error, non-zero otherwise. void ptytty_login (PTYTTY ptytty, int cmd_pid, bool login_shell, const char *hostname) See "pty->login". void ptytty_close_tty (PTYTTY ptytty) See "pty->close_tty". int ptytty_make_controlling_tty (PTYTTY ptytty) See "pty->make_controlling_tty". void ptytty_set_utf8_mode (PTYTTY ptytty, int on) See "pty->set_utf8_mode". void ptytty_drop_privileges () See "ptytty::drop_privileges". void ptytty_use_helper () See "ptytty::use_helper". PORTABILITY To date, libptytty has been tested on the following platforms: GNU/Linux FreeBSD NetBSD OpenBSD macOS Solaris AIX BUGS You kiddin'? AUTHORS Emanuele Giaquinta , Marc Alexander Lehmann . libptytty-2.0/COPYING0000644000175000017500000004312212376705067013761 0ustar giaqemgiaqem GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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. Copyright (C) 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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. , 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.