--- /dev/null
+ 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 Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 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.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
--- /dev/null
+# config
+srcdir = .
+VPATH = $(srcdir)
+-include Make.config
+include $(srcdir)/mk/Variables.mk
+
+CFLAGS += -Wall -Wno-pointer-sign
+CFLAGS += -DVERSION='"$(VERSION)"'
+
+TARGETS := amtterm
+DESKTOP := $(wildcard *.desktop)
+
+all: build
+
+#################################################################
+# poor man's autoconf ;-)
+
+include mk/Autoconf.mk
+
+define make-config
+LIB := $(LIB)
+HAVE_GTK := $(call ac_pkg_config,gtk+-x11-2.0)
+HAVE_VTE := $(call ac_pkg_config,vte)
+endef
+
+#################################################################
+
+# build gamt?
+ifeq ($(HAVE_GTK)$(HAVE_VTE),yesyes)
+ TARGETS += gamt
+ gamt : CFLAGS += -Wno-strict-prototypes
+ gamt : pkglst += gtk+-x11-2.0 vte
+endif
+
+CFLAGS += $(shell test "$(pkglst)" != "" && pkg-config --cflags $(pkglst))
+LDLIBS += $(shell test "$(pkglst)" != "" && pkg-config --libs $(pkglst))
+
+#################################################################
+
+build: $(TARGETS)
+
+install: build
+ $(INSTALL_DIR) $(bindir) $(appdir) $(mandir)/man1 $(mandir)/man7
+ $(INSTALL_BINARY) $(TARGETS) $(bindir)
+ $(INSTALL_SCRIPT) amttool $(bindir)
+ $(INSTALL_DATA) $(DESKTOP) $(appdir)
+ $(INSTALL_DATA) gamt.man $(mandir)/man1/gamt.1
+ $(INSTALL_DATA) amtterm.man $(mandir)/man1/amtterm.1
+ $(INSTALL_DATA) amttool.man $(mandir)/man1/amttool.1
+ $(INSTALL_DATA) amt-howto.man $(mandir)/man7/amt-howto.7
+
+clean:
+ rm -f *.o *~
+ rm -f $(TARGETS)
+
+distclean: clean
+ rm -f Make.config
+
+#################################################################
+
+amtterm: amtterm.o redir.o tcp.o
+gamt: gamt.o redir.o tcp.o parseconfig.o
+
+#################################################################
+
+include mk/Compile.mk
+include mk/Maintainer.mk
+-include $(depfiles)
--- /dev/null
+
+howto compile and install this package
+======================================
+
+
+really short install instructions
+---------------------------------
+
+ $ make
+ $ su -c "make install"
+
+
+
+the more detailed version
+-------------------------
+
+Make sure you use GNU make. The file name "GNUmakefile" isn't a joke,
+this package really requires GNU make.
+
+As first step make will do some config checks on your system and write
+the results to Make.config. If you want to have a look at Make.config
+before the actual build starts you can run this step separately using
+"make config".
+
+The Makefiles use the usual GNU-ish Makefile conventions for variable
+names and default values, i.e. prefix=/usr/local, ...
+
+The values for some frequently adapted variables are initialized from
+the enviroment. Thus you can change the defaults simply by setting
+environment variables:
+
+ $ prefix="/usr"
+ $ CFLAGS="-O3 -mcpu=i686"
+ $ export prefix CFLAGS
+
+Almost any variable can be overridden on the make command line. It is
+often used this way to install into some buildroot for packaging ...
+
+ $ su -c "make DESTDIR=/tmp/buildroot install"
+
+... but it works for most other variables equally well. There are
+some exceptions through, it usually does _not_ work for CFLAGS for
+example.
+
+Try "make verbose=yes" if you want to see the complete command lines
+executed by make instead of the short messages (for trouble shooting,
+because you like this way, for whatever reason ...). This also makes
+the config checks performed by "make config" more verbose.
+
+If you don't trust my Makefiles you can run "make -n install" to see
+what "make install" would do on your system. It will produce
+human-readable output (unlike automake ...).
+
+Have fun,
+
+ Gerd
+
+--
+Gerd Hoffmann <kraxel@suse.de>
--- /dev/null
+#ifndef __REDIRECTION_CONSTANTS__\r
+#define __REDIRECTION_CONSTANTS__\r
+\r
+#define STATUS_SUCCESS 0x00\r
+#define SOL_FIRMWARE_REV_MAJOR 0x01\r
+#define SOL_FIRMWARE_REV_MINOR 0x00\r
+\r
+//Session Manager Messages Formats\r
+#define START_REDIRECTION_SESSION 0x10\r
+#define START_REDIRECTION_SESSION_REPLY 0x11\r
+#define END_REDIRECTION_SESSION 0x12\r
+#define AUTHENTICATE_SESSION 0x13\r
+#define AUTHENTICATE_SESSION_REPLY 0x14\r
+\r
+#define START_REDIRECTION_SESSION_LENGTH 8\r
+#define START_REDIRECTION_SESSION_REPLY_LENGTH 13\r
+#define END_REDIRECTION_SESSION_LENGTH 4\r
+\r
+//SOL Messages Formats\r
+#define START_SOL_REDIRECTION 0x20\r
+#define START_SOL_REDIRECTION_REPLY 0x21\r
+#define END_SOL_REDIRECTION 0x22\r
+#define END_SOL_REDIRECTION_REPLY 0x23\r
+#define SOL_KEEP_ALIVE_PING 0x24 //Console to Host\r
+#define SOL_KEEP_ALIVE_PONG 0x25 //Host to Console\r
+#define SOL_DATA_TO_HOST 0x28 //Console to host\r
+#define SOL_DATA_FROM_HOST 0x2A //Host to Console\r
+#define SOL_HEARTBEAT 0x2B\r
+\r
+#define HEARTBEAT_LENGTH 8\r
+#define START_SOL_REDIRECTION_LENGTH 24\r
+#define START_SOL_REDIRECTION_REPLY_LENGTH 23 //TODO: There is a OEM Defined data field that we are assuming to be 0 bytes..\r
+#define END_SOL_REDIRECTION_LENGTH 8\r
+#define END_SOL_REDIRECTION_REPLY_LENGTH 8\r
+\r
+//IDER Messages Formats\r
+#define START_IDER_REDIRECTION 0x40\r
+#define START_IDER_REDIRECTION_REPLY 0x41\r
+#define END_IDER_REDIRECTION 0x42\r
+#define END_IDER_REDIRECTION_REPLY 0x43\r
+#define IDER_KEEP_ALIVE_PING 0x44 //Console to Host\r
+#define IDER_KEEP_ALIVE_PONG 0x45 //Host to Console\r
+#define IDER_RESET_OCCURED 0x46\r
+#define IDER_RESET_OCCURED_RESPONSE 0x47\r
+#define IDER_DISABLE_ENABLE_FEATURES 0x48\r
+#define IDER_DISABLE_ENABLE_FEATURES_REPLY 0x49\r
+#define IDER_HEARTBEAT 0x4B\r
+#define IDER_COMMAND_WRITTEN 0x50\r
+#define IDER_COMMAND_END_RESPONSE 0x51\r
+#define IDER_GET_DATA_FROM_HOST 0x52\r
+#define IDER_DATA_FROM_HOST 0x53\r
+#define IDER_DATA_TO_HOST 0x54\r
+\r
+#define START_IDER_REDIRECTION_LENGTH 18\r
+#define START_IDER_REDIRECTION_REPLY_LENGTH 30 //TODO: There is a OEM Defined data field that we are assuming to be 0 bytes..\r
+#define END_IDER_REDIRECTION_LENGTH 8\r
+#define END_IDER_REDIRECTION_REPLY_LENGTH 8\r
+#define IDER_RESET_OCCURED_LENGTH 9\r
+#define IDER_RESET_OCCURED_RESPONSE_LENGTH 8\r
+#define IDER_DISABLE_ENABLE_FEATURES_REPLY_LENGTH 13\r
+#define IDER_COMMAND_END_RESPONSE_LENGTH 31\r
+#define IDER_GET_DATA_FROM_HOST_LENGTH 31\r
+\r
+static const unsigned int SOL_SESSION = 0x204C4F53;\r
+static const unsigned int IDER_SESSION = 0x52454449;\r
+\r
+static const unsigned short MAX_TRANSMIT_BUFFER = 1000;\r
+static const unsigned short TRANSMIT_BUFFER_TIMEOUT = 100;\r
+static const unsigned short TRANSMIT_OVERFLOW_TIMEOUT = 0;\r
+static const unsigned short HOST_SESSION_RX_TIMEOUT = 10000;\r
+static const unsigned short HOST_FIFO_RX_FLUSH_TIMEOUT = 0;\r
+static const unsigned short HEARTBEAT_INTERVAL = 5000;\r
+\r
+static const unsigned int SESSION_MANAGER_OEM_IANA_NUMBER = 0x5555; //TODO: Test \r
+static const unsigned int SOL_OEM_IANA_NUMBER = 0x6666; //TODO: Test\r
+\r
+static const unsigned short RECEIVE_BUFFER_SIZE = 0x100;\r
+\r
+#endif\r
--- /dev/null
+.TH amt-howto 7 "(c) 2007 Gerd Hoffmann"
+.SH SYNOPSIS
+Intel AMT with linux mini howto
+.SH DESCRIPTION
+
+.SS What is AMT and why I should care?
+AMT stands for "Active Management Technology". It provides some
+remote management facilities. They are handled by the hardware and
+firmware, thus they work independant from the operation system.
+Means: It works before Linux bootet up to the point where it activated
+the network interface. It works even when your most recent test
+kernel deadlocked the machine. Which makes it quite useful for
+development machines ...
+.P
+Intel AMT is part of the vPro Platform. Recent intel-chipset based
+business machines should have it. My fairly new Intel SDV machine has
+it too.
+
+.SS Documentation
+Look here for documentation beyond this mini howto:
+.br
+http://www.intel.com/technology/platform-technology/intel-amt/engage.htm
+.br
+Most useful to get started: "Intel AMT Deployment and Reference Guide"
+
+.SS Very short AMT enabling instructions.
+.TP
+Enter BIOS Setup.
+* Enable AMT
+.TP
+Enter ME (Management Extention) Setup. Ctrl-P hotkey works for me.
+* Login, factory default password is "admin".
+.br
+* Change password. Trivial ones don't work, must include upper-
+and lowercase letters, digits, special characters.
+.br
+* Enable AMT Managment.
+.TP
+Reboot, Enter ME Setup again with AMT enabled.
+* Configure AMT (hostname, network config, ...)
+.br
+* Use SMB (Small Business) management mode. The other one
+(Enterprise) requires Active Directory Service Infrastructure,
+you don't want that, at least not for your first steps ...
+
+.SS Testing AMT
+Take your browser, point it to http://machine:16992/. If you
+configured AMT to use DHCP (which is the default) the OS and the
+management stack share the same IP address.
+.P
+You must do that from a remote host as the NIC intercepts network
+packets for AMT, thus it doesn't work from the local machine as the
+packets never pass the NIC then. If everything is fine you'll see a
+greeting page with a button for login.
+.P
+You can login now, using "admin" as username and the password
+configured during setup. You'll see some pages with informations
+about the machine. You can also change AMT settings here.
+
+.SS Control Machine
+You might have noticed already while browing the pages: There is a
+"Remote Control" page. You can remotely reset and powercycle the
+machine there, thus recover the machine after booting a b0rken kernel,
+without having someone walk over to the machine and hit the reset
+button.
+
+.SS Serial-over-LAN (SOL) console
+AMT also provides a virtual serial port which can be accessed via
+network. That gives you a serial console without a serial cable to
+another machine.
+.P
+If you have activated AMT and SOL the linux kernel should see an
+additional serial port, like this on my machine:
+.P
+.nf
+ [root@xeni ~]# dmesg | grep ttyS2
+ 0000:00:03.3: ttyS2 at I/O 0xe000 (irq = 169) is a 16550A
+.fi
+.P
+Edit initab, add a line like this:
+.P
+.nf
+ S2:2345:respawn:/sbin/agetty ttyS2 115200 vt100-nav
+.fi
+.P
+You should add the serial port to /etc/securetty too so you are able
+to login as root. Reload inittab ("init q"). Use amtterm to connect.
+Tap enter. You should see a login prompt now and be able to login.
+.P
+You can also use that device as console for the linux kernel, using
+the usual "console=ttyS2,115200" kernel command line argument, so you
+see the boot messages (and kernel Oopses, if any).
+.P
+You can tell grub to use that serial device, so you can pick a working
+kernel for the next boot. Usual commands from the grub manual, except
+that you need "--port=0xe000" instead of "--unit=0" due to the
+non-standard I/O port for the serial line (my machine, yours might use
+another port, check linux kernel boot messages).
+.P
+The magic command for the Xen kernel is "com1=115200,8n1,0xe000,0"
+(again, you might have to replace the I/O port). The final '0'
+disables the IRQ, otherwise the Xen kernel hangs at boot after
+enabling interrupts.
+
+.SS Fun with Xen and AMT
+The AMT network stack seems to become slightly confused when running
+on a Xen host in DHCP mode. Everything works fine as long as only
+Dom0 runs. But if one starts a guest OS (with bridged networking) AMT
+suddenly changes the IP address to the one the guest aquired via DHCP.
+.P
+It is probably a good idea to assign a separate static IP address to
+AMT then. I didn't manage to switch my machine from DHCP to static IP
+yet though, the BIOS refuses to accept the settings. The error
+message doesn't indicate why.
+
+.SS More fun with AMT
+You might want to download the DTK (Developer Toolkit, source code is
+available too) and play with it. The .exe is a self-extracting rar
+archive and can be unpacked on linux using the unrar utility. The
+Switchbox comes with a linux binary (additionally to the Windows
+stuff). The GUI tools are written in C#. Trying to make them fly
+with mono didn't work for me though (mono version 1.2.3 as shipped
+with Fedora 7).
+
+.SH SEE ALSO
+amtterm(1), gamt(1), amttool(1)
+.P
+http://www.intel.com/technology/platform-technology/intel-amt/
+.SH WRITTEN BY
+Gerd Hoffmann <kraxel@redhat.com>
--- /dev/null
+/*
+ * amtterm -- Intel AMT serial-over-lan client, console version.
+ *
+ * Copyright (C) 2007 Gerd Hoffmann <kraxel@redhat.com
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+
+#include "redir.h"
+
+#define APPNAME "amtterm"
+#define BUFSIZE 512
+
+/* ------------------------------------------------------------------ */
+
+static int recv_tty(void *cb_data, unsigned char *buf, int len)
+{
+// struct redir *r = cb_data;
+
+ return write(0, buf, len);
+}
+
+static void state_tty(void *cb_data, enum redir_state old, enum redir_state new)
+{
+ struct redir *r = cb_data;
+
+ if (r->verbose)
+ fprintf(stderr, APPNAME ": %s -> %s (%s)\n",
+ redir_state_name(old), redir_state_name(new),
+ redir_state_desc(new));
+ switch (new) {
+ case REDIR_RUN_SOL:
+ if (r->verbose)
+ fprintf(stderr,
+ "serial-over-lan redirection ok\n"
+ "connected now, use ^] to escape\n");
+ break;
+ case REDIR_ERROR:
+ fprintf(stderr, APPNAME ": ERROR: %s\n", r->err);
+ break;
+ default:
+ break;
+ }
+}
+
+static int redir_loop(struct redir *r)
+{
+ unsigned char buf[BUFSIZE+1];
+ struct timeval tv;
+ int rc;
+ fd_set set;
+
+ for(;;) {
+ if (r->state == REDIR_CLOSED ||
+ r->state == REDIR_ERROR)
+ break;
+
+ FD_ZERO(&set);
+ if (r->state == REDIR_RUN_SOL)
+ FD_SET(0,&set);
+ FD_SET(r->sock,&set);
+ tv.tv_sec = HEARTBEAT_INTERVAL * 4 / 1000;
+ tv.tv_usec = 0;
+ switch (select(r->sock+1,&set,NULL,NULL,&tv)) {
+ case -1:
+ perror("select");
+ return -1;
+ case 0:
+ fprintf(stderr,"select: timeout\n");
+ return -1;
+ }
+
+ if (FD_ISSET(0,&set)) {
+ /* stdin has data */
+ rc = read(0,buf,BUFSIZE);
+ switch (rc) {
+ case -1:
+ perror("read(stdin)");
+ return -1;
+ case 0:
+ fprintf(stderr,"EOF from stdin\n");
+ return -1;
+ default:
+ if (buf[0] == 0x1d) {
+ if (r->verbose)
+ fprintf(stderr, "\n" APPNAME ": saw ^], exiting\n");
+ redir_sol_stop(r);
+ }
+ if (-1 == redir_sol_send(r, buf, rc))
+ return -1;
+ break;
+ }
+ }
+
+ if (FD_ISSET(r->sock,&set)) {
+ if (-1 == redir_data(r))
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+struct termios saved_attributes;
+int saved_fl;
+
+static void tty_save(void)
+{
+ fcntl(0,F_GETFL,&saved_fl);
+ tcgetattr (0, &saved_attributes);
+}
+
+static void tty_noecho(void)
+{
+ struct termios tattr;
+
+ memcpy(&tattr,&saved_attributes,sizeof(struct termios));
+ tattr.c_lflag &= ~(ECHO);
+ tcsetattr (0, TCSAFLUSH, &tattr);
+}
+
+static void tty_raw(void)
+{
+ struct termios tattr;
+
+ fcntl(0,F_SETFL,O_NONBLOCK);
+ memcpy(&tattr,&saved_attributes,sizeof(struct termios));
+ tattr.c_lflag &= ~(ISIG|ICANON|ECHO);
+ tattr.c_cc[VMIN] = 1;
+ tattr.c_cc[VTIME] = 0;
+ tcsetattr (0, TCSAFLUSH, &tattr);
+}
+
+static void tty_restore(void)
+{
+ fcntl(0,F_SETFL,saved_fl);
+ tcsetattr (0, TCSANOW, &saved_attributes);
+}
+
+/* ------------------------------------------------------------------ */
+
+static void usage(FILE *fp)
+{
+ fprintf(fp,
+ "\n"
+ "This is " APPNAME ", release " VERSION ", I'll establish\n"
+ "serial-over-lan (sol) connections to your Intel AMT boxes.\n"
+ "\n"
+ "usage: " APPNAME " [options] host [port]\n"
+ "options:\n"
+ " -h print this text\n"
+ " -v verbose (default)\n"
+ " -q quiet\n"
+ " -u user username (default: admin)\n"
+ " -p pass password (default: $AMT_PASSWORD)\n"
+ "\n"
+ "By default port 16994 is used.\n"
+ "If no password is given " APPNAME " will ask for one.\n"
+ "\n"
+ "-- \n"
+ "(c) 2007 Gerd Hoffmann <kraxel@redhat.com>\n"
+ "\n");
+}
+
+int main(int argc, char *argv[])
+{
+ struct redir r;
+ char *h;
+ int c;
+
+ memset(&r, 0, sizeof(r));
+ r.verbose = 1;
+ memcpy(r.type, "SOL ", 4);
+ strcpy(r.user, "admin");
+
+ r.cb_data = &r;
+ r.cb_recv = recv_tty;
+ r.cb_state = state_tty;
+
+ if (NULL != (h = getenv("AMT_PASSWORD")))
+ snprintf(r.pass, sizeof(r.pass), "%s", h);
+
+ for (;;) {
+ if (-1 == (c = getopt(argc, argv, "hvqu:p:")))
+ break;
+ switch (c) {
+ case 'v':
+ r.verbose = 1;
+ break;
+ case 'q':
+ r.verbose = 0;
+ break;
+ case 'u':
+ snprintf(r.user, sizeof(r.user), "%s", optarg);
+ break;
+ case 'p':
+ snprintf(r.pass, sizeof(r.pass), "%s", optarg);
+ memset(optarg,'*',strlen(optarg)); /* rm passwd from ps list */
+ break;
+
+ case 'h':
+ usage(stdout);
+ exit(0);
+ default:
+ usage(stderr);
+ exit(1);
+ }
+ }
+
+ if (optind < argc)
+ snprintf(r.host, sizeof(r.host), "%s", argv[optind]);
+ if (optind+1 < argc)
+ snprintf(r.port, sizeof(r.port), "%s", argv[optind+1]);
+ if (0 == strlen(r.host)) {
+ usage(stderr);
+ exit(1);
+ }
+
+ tty_save();
+ if (0 == strlen(r.pass)) {
+ tty_noecho();
+ fprintf(stderr, "AMT password for host %s: ", r.host);
+ fgets(r.pass, sizeof(r.pass), stdin);
+ fprintf(stderr, "\n");
+ if (NULL != (h = strchr(r.pass, '\r')))
+ *h = 0;
+ if (NULL != (h = strchr(r.pass, '\n')))
+ *h = 0;
+ }
+
+ if (-1 == redir_connect(&r)) {
+ tty_restore();
+ exit(1);
+ }
+
+ tty_raw();
+ redir_start(&r);
+ redir_loop(&r);
+ tty_restore();
+
+ exit(0);
+}
--- /dev/null
+.TH amtterm 1 "(c) 2007 Gerd Hoffmann"
+.SH NAME
+amtterm - Intel AMT serial-over-lan (sol) client.
+.SH SYNOPSIS
+.B amtterm [ options ] host [ port ]
+.SH DESCRIPTION
+.B amtterm
+provides access to the serial-over-lan port of Intel AMT managed
+machines.
+.B host
+is the hostname or IP address of the machine amtterm should connect
+to.
+.B port
+is the tcp port to use and defaults to 16994 (standard AMT redirection
+port) if unspecified.
+.P
+For more inforamtions on Intel AMT check amt-howto(7).
+.SH OPTIONS
+.TP
+.B -h
+Display help text.
+.TP
+.B -v
+Be verbose (default).
+.TP
+.B -q
+Be quiet.
+.TP
+.B -u <user>
+Specify username, defaults to "admin".
+.TP
+.B -p <pass>
+Specify password.
+.B amtterm
+will prompt on the terminal if unspecified.
+.SH ENVIRONMENT
+.TP
+.B AMT_PASSWORD
+Default value for the password.
+.SH SEE ALSO
+gamt(1), amttool(1), amt-howto(7)
+.SH AUTHOR
+(c) 2007 Gerd Hoffmann <kraxel@redhat.com>
--- /dev/null
+#!/usr/bin/perl
+use strict;
+use warnings;
+use SOAP::Lite;
+#use SOAP::Lite +trace => 'all';
+
+my $amt_host = shift;
+my $amt_port = "16992";
+$main::amt_user = "admin";
+$main::amt_pass = $ENV{'AMT_PASSWORD'};
+my $amt_debug = 0;
+$amt_debug = $ENV{'AMT_DEBUG'} if defined($ENV{'AMT_DEBUG'});
+
+my $amt_command = shift;
+$amt_command = "info" if !defined($amt_command);
+
+my $amt_version;
+
+#############################################################################
+# data
+
+my @ps = ("S0", "S1", "S2", "S3", "S4", "S5 (soft-off)", "S4/S5", "Off");
+my %rcc = (
+ "reset" => "16",
+ "powerup" => "17",
+ "powerdown" => "18",
+ "powercycle" => "19",
+);
+
+# incomplete list
+my %pt_status = (
+ 0x0 => "success",
+ 0x1 => "internal error",
+ 0x3 => "invalid pt_mode",
+ 0xc => "invalid name",
+ 0xf => "invalid byte_count",
+ 0x10 => "not permitted",
+ 0x17 => "max limit_reached",
+ 0x18 => "invalid auth_type",
+ 0x1a => "invalid dhcp_mode",
+ 0x1b => "invalid ip_address",
+ 0x1c => "invalid domain_name",
+ 0x20 => "invalid provisioning_state",
+ 0x22 => "invalid time",
+ 0x23 => "invalid index",
+ 0x24 => "invalid parameter",
+ 0x25 => "invalid netmask",
+ 0x26 => "flash write_limit_exceeded",
+ 0x800 => "network if_error_base",
+ 0x801 => "unsupported oem_number",
+ 0x802 => "unsupported boot_option",
+ 0x803 => "invalid command",
+ 0x804 => "invalid special_command",
+ 0x805 => "invalid handle",
+ 0x806 => "invalid password",
+ 0x807 => "invalid realm",
+ 0x808 => "storage acl_entry_in_use",
+ 0x809 => "data missing",
+ 0x80a => "duplicate",
+ 0x80b => "eventlog frozen",
+ 0x80c => "pki missing_keys",
+ 0x80d => "pki generating_keys",
+ 0x80e => "invalid key",
+ 0x80f => "invalid cert",
+ 0x810 => "cert key_not_match",
+ 0x811 => "max kerb_domain_reached",
+ 0x812 => "unsupported",
+ 0x813 => "invalid priority",
+ 0x814 => "not found",
+ 0x815 => "invalid credentials",
+ 0x816 => "invalid passphrase",
+ 0x818 => "no association",
+);
+
+
+#############################################################################
+# soap setup
+
+my ($nas, $sas, $rcs);
+
+sub SOAP::Transport::HTTP::Client::get_basic_credentials {
+ return $main::amt_user => $main::amt_pass;
+}
+
+sub soap_init() {
+ my $proxybase = "http://$amt_host:$amt_port";
+ my $schemabase = "http://schemas.intel.com/platform/client";
+
+ $nas = SOAP::Lite->new(
+ proxy => "$proxybase/NetworkAdministrationService",
+ default_ns => "$schemabase/NetworkAdministration/2004/01");
+ $sas = SOAP::Lite->new(
+ proxy => "$proxybase/SecurityAdministrationService",
+ default_ns => "$schemabase/SecurityAdministration/2004/01");
+ $rcs = SOAP::Lite->new(
+ proxy => "$proxybase/RemoteControlService",
+ default_ns => "$schemabase/RemoteControl/2004/01");
+
+ $nas->autotype(0);
+ $sas->autotype(0);
+ $rcs->autotype(0);
+
+ $amt_version = $sas->GetCoreVersion()->paramsout;
+}
+
+
+#############################################################################
+# functions
+
+sub usage() {
+ print STDERR <<EOF;
+
+This utility can talk to Intel AMT managed machines.
+
+usage: amttool <hostname> [ <command> ]
+commands:
+ info - print some machine info (default).
+ reset - reset machine.
+ powerup - turn on machine.
+ powerdown - turn off machine.
+ powercycle - powercycle machine.
+
+AMT 2.5+ only:
+ netinfo - print network config.
+ netconf <args> - configure network (check manpage).
+
+Password is passed via AMT_PASSWORD environment variable.
+
+EOF
+}
+
+sub print_result($) {
+ my $ret = shift;
+ my $rc = $ret->result;
+ my $msg;
+
+ if (!defined($rc)) {
+ $msg = "soap failure";
+ } elsif (!defined($pt_status{$rc})) {
+ $msg = sprintf("unknown pt_status code: 0x%x", $rc);
+ } else {
+ $msg = "pt_status: " . $pt_status{$rc};
+ }
+ printf "result: %s\n", $msg;
+}
+
+sub print_paramsout($) {
+ my $ret = shift;
+ my @paramsout = $ret->paramsout;
+ print "params: " . join(", ", @paramsout) . "\n";
+}
+
+sub print_hash {
+ my $hash = shift;
+ my $in = shift;
+ my $wi = shift;
+
+ foreach my $item (sort keys %{$hash}) {
+ if (ref($hash->{$item}) eq "HASH") {
+# printf "%*s%s\n", $in, "", $item;
+ next;
+ }
+ printf "%*s%-*s%s\n", $in, "", $wi, $item, $hash->{$item};
+ }
+}
+
+sub print_hash_ipv4 {
+ my $hash = shift;
+ my $in = shift;
+ my $wi = shift;
+
+ foreach my $item (sort keys %{$hash}) {
+ my $addr = sprintf("%d.%d.%d.%d",
+ $hash->{$item} / 256 / 256 / 256,
+ $hash->{$item} / 256 / 256 % 256,
+ $hash->{$item} / 256 % 256,
+ $hash->{$item} % 256);
+ printf "%*s%-*s%s\n", $in, "", $wi, $item, $addr;
+ }
+}
+
+sub do_soap {
+ my $soap = shift;
+ my $name = shift;
+ my @args = @_;
+ my $method;
+
+ $method = SOAP::Data->name($name)
+ ->attr( { xmlns => $soap->ns } );
+
+ if ($amt_debug) {
+ print "-- \n";
+ open XML, "| xmllint --format -";
+ print XML $soap->serializer->envelope(method => $method, @_);
+ close XML;
+ print "-- \n";
+ }
+
+ my $ret = $soap->call($method, @args);
+ print_result($ret);
+ return $ret;
+}
+
+sub check_amt_version {
+ my $major = shift;
+ my $minor = shift;
+
+ $amt_version =~ m/^(\d+).(\d+)/;
+ return if $1 > $major;
+ return if $1 == $major && $2 >= $minor;
+ die "version mismatch (need >= $major.$minor, have $amt_version)";
+}
+
+sub print_general_info() {
+ printf "### AMT info on machine '%s' ###\n", $amt_host;
+
+ printf "AMT version: %s\n", $amt_version;
+
+ my $hostname = $nas->GetHostName()->paramsout;
+ my $domainname = $nas->GetDomainName()->paramsout;
+ printf "Hostname: %s.%s\n", $hostname, $domainname;
+
+ my $powerstate = $rcs->GetSystemPowerState()->paramsout;
+ printf "Powerstate: %s\n", $ps [ $powerstate & 0x0f ];
+}
+
+sub print_remote_info() {
+ my @rccaps = $rcs->GetRemoteControlCapabilities()->paramsout;
+ printf "Remote Control Capabilities:\n";
+ printf " IanaOemNumber %x\n", $rccaps[0];
+ printf " OemDefinedCapabilities %s%s%s%s%s\n",
+ $rccaps[1] & 0x01 ? "IDER " : "",
+ $rccaps[1] & 0x02 ? "SOL " : "",
+ $rccaps[1] & 0x04 ? "BiosReflash " : "",
+ $rccaps[1] & 0x08 ? "BiosSetup " : "",
+ $rccaps[1] & 0x10 ? "BiosPause " : "";
+
+ printf " SpecialCommandsSupported %s%s%s%s%s\n",
+ $rccaps[2] & 0x0100 ? "PXE-boot " : "",
+ $rccaps[2] & 0x0200 ? "HD-boot " : "",
+ $rccaps[2] & 0x0400 ? "HD-boot-safemode " : "",
+ $rccaps[2] & 0x0800 ? "diag-boot " : "",
+ $rccaps[2] & 0x1000 ? "cd-boot " : "";
+
+ printf " SystemCapabilitiesSupported %s%s%s%s\n",
+ $rccaps[3] & 0x01 ? "powercycle " : "",
+ $rccaps[3] & 0x02 ? "powerdown " : "",
+ $rccaps[3] & 0x03 ? "powerup " : "",
+ $rccaps[3] & 0x04 ? "reset " : "";
+
+ printf " SystemFirmwareCapabilities %x\n", $rccaps[4];
+}
+
+sub print_network_info() {
+ my $ret;
+
+ $ret = $nas->EnumerateInterfaces();
+ my @if = $ret->paramsout;
+ foreach my $if (@if) {
+ printf "Network Interface %s:\n", $if;
+ my $arg = SOAP::Data->name('InterfaceHandle' => $if);
+ $ret = $nas->GetInterfaceSettings($arg);
+ my $desc = $ret->paramsout;
+ print_hash($ret->paramsout, 4, 32);
+ print_hash_ipv4($ret->paramsout->{'IPv4Parameters'}, 8, 28);
+ }
+}
+
+sub remote_control($) {
+ my $command = shift;
+ my @args;
+
+ my $hostname = $nas->GetHostName()->paramsout;
+ my $domainname = $nas->GetDomainName()->paramsout;
+ printf "host %s.%s, %s [y/N] ? ", $hostname, $domainname, $command;
+ my $reply = <>;
+ if ($reply =~ m/^(y|yes)$/i) {
+ printf "execute: %s\n", $command;
+ push (@args, SOAP::Data->name('Command' => $rcc{$command}));
+ push (@args, SOAP::Data->name('IanaOemNumber' => 4542));
+ do_soap($rcs, "RemoteControl", @args);
+ } else {
+ printf "canceled\n";
+ }
+}
+
+sub ipv4_addr($$) {
+ my $name = shift;
+ my $ipv4 = shift;
+
+ $ipv4 =~ m/(\d+).(\d+).(\d+).(\d+)/ or die "parse ipv4 address: $ipv4";
+ my $num = $1 * 256 * 256 * 256 +
+ $2 * 256 * 246 +
+ $3 * 256 +
+ $4;
+ printf STDERR "ipv4 %-24s: %-16s -> %d\n", $name, $ipv4, $num
+ if $amt_debug;
+ return SOAP::Data->name($name => $num);
+}
+
+sub configure_network {
+ my $if = shift;
+ my $link = shift;
+ my $ip = shift;
+ my $mask = shift;
+ my $gw = shift;
+ my $dns1 = shift;
+ my $dns2 = shift;
+
+ my $mode;
+ my @ifdesc;
+ my @ipv4;
+
+ my $method;
+ my @args;
+
+ # build argument structs ...
+ die "no interface" if !defined($if);
+ die "no linkpolicy" if !defined($link);
+ if (defined($ip)) {
+ $mode = "SEPARATE_MAC_ADDRESS";
+ die "no ip mask" if !defined($mask);
+ die "no default gw" if !defined($gw);
+ $dns1 = $gw if !defined($dns1);
+ $dns2 = "0.0.0.0" if !defined($dns2);
+ push (@ipv4, ipv4_addr("LocalAddress", $ip));
+ push (@ipv4, ipv4_addr("SubnetMask", $mask));
+ push (@ipv4, ipv4_addr("DefaultGatewayAddress", $gw));
+ push (@ipv4, ipv4_addr("PrimaryDnsAddress", $dns1));
+ push (@ipv4, ipv4_addr("SecondaryDnsAddress", $dns2));
+ } else {
+ $mode = "SHARED_MAC_ADDRESS";
+ # no ip info -- use DHCP
+ }
+
+ push (@ifdesc, SOAP::Data->name("InterfaceMode" => $mode));
+ push (@ifdesc, SOAP::Data->name("LinkPolicy" => $link));
+ push (@ifdesc, SOAP::Data->name("IPv4Parameters" =>
+ \SOAP::Data->value(@ipv4)))
+ if @ipv4;
+
+ push (@args, SOAP::Data->name("InterfaceHandle" => $if));
+ push (@args, SOAP::Data->name("InterfaceDescriptor" =>
+ \SOAP::Data->value(@ifdesc)));
+
+ # perform call
+ do_soap($nas, "SetInterfaceSettings", @args);
+}
+
+
+#############################################################################
+# main
+
+if (!defined($amt_host)) {
+ usage();
+ exit 1;
+}
+
+soap_init;
+
+if ($amt_command eq "info") {
+ print_general_info;
+ print_remote_info;
+} elsif ($amt_command eq "netinfo") {
+ check_amt_version(2,5);
+ print_network_info;
+} elsif ($amt_command eq "netconf") {
+ check_amt_version(2,5);
+ configure_network(@ARGV);
+} elsif ($amt_command =~ m/^(reset|powerup|powerdown|powercycle)$/) {
+ remote_control($amt_command);
+} else {
+ print "unknown command: $amt_command\n";
+}
+
--- /dev/null
+.TH amttool 1 "(c) 2007 Gerd Hoffmann"
+.SH NAME
+amttool - remotely control Intel AMT managed machines.
+.SH SYNOPSIS
+.B amttool host [ command ]
+.SH DESCRIPTION
+.B amttool
+is a perl script which speaks SOAP to Intel AMT managed machines.
+It can query informations about the machine in question and also
+send some commands for basic remote control.
+.P
+.B host
+is the hostname or IP address of the machine amttool should
+control.
+.B command
+is an optional command.
+.P
+You must set fill AMT_PASSWORD environment variable with the AMT
+password.
+.P
+For more inforamtions on Intel AMT check amt-howto(7).
+.SH COMMANDS
+.TP
+.B info
+gather information (default).
+.TP
+.B reset
+reset machine.
+.TP
+.B powerup
+turn on machine.
+.TP
+.B powerdown
+turn off machine.
+.TP
+.B powercycle
+powercycle machine.
+.TP
+.B netinfo
+print network configuration (requires AMT 2.5+).
+.TP
+.B netconf if link [ ip mask gw [ dns1 [ dns2 ]]]
+configure network interface (requires AMT 2.5+).
+.B if
+is the interface handle,
+.B link
+the link policy. If in doubt just feed in what the netinfo command
+prints.
+.B ip
+is the IPv4 address,
+.B mask
+the netmask,
+.B gw
+the default gateway,
+.B dns1
+and
+.B dns2
+are the DNS Servers. If no IP configuration is specified the tool
+tries to configure the machine in shared mac address mode with dhcp,
+otherwise in separate mac address mode with static IP address.
+Default for dns1 is the gateway address, for dns2 it is 0.0.0.0.
+.SH ENVIRONMENT
+.TP
+.B AMT_PASSWORD
+AMT password.
+.TP
+.B AMT_DEBUG
+Enable debug output.
+.SH SEE ALSO
+amtterm(1), gamt(1), amt-howto(7)
+.SH AUTHOR
+(c) 2007 Gerd Hoffmann <kraxel@redhat.com>
--- /dev/null
+/*
+ * amtterm -- Intel AMT serial-over-lan client, gtk version.
+ *
+ * Copyright (C) 2007 Gerd Hoffmann <kraxel@redhat.com
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <locale.h>
+#include <signal.h>
+#include <ctype.h>
+
+#include <gdk/gdk.h>
+#include <gdk/gdkx.h>
+#include <gtk/gtk.h>
+#include <vte/vte.h>
+
+#include "parseconfig.h"
+#include "redir.h"
+
+#define APPNAME "gamt"
+
+struct gamt_window {
+ /* gtk stuff */
+ GtkActionGroup *ag;
+ GtkUIManager *ui;
+ GtkWidget *win;
+ GtkWidget *vte;
+ GtkWidget *status;
+ GtkWidget *icon;
+
+ GtkActionGroup *hosts_ag;
+ guint hosts_id;
+
+ /* sol stuff */
+ struct redir redir;
+ GIOChannel *ch;
+ guint id;
+};
+
+static const char *state_stock[] = {
+ [ REDIR_NONE ] = GTK_STOCK_DISCONNECT,
+#if 0
+ [ REDIR_CONNECT ] = GTK_STOCK_,
+ [ REDIR_INIT ] = GTK_STOCK_,
+ [ REDIR_AUTH ] = GTK_STOCK_,
+ [ REDIR_INIT_SOL ] = GTK_STOCK_,
+#endif
+ [ REDIR_RUN_SOL ] = GTK_STOCK_CONNECT,
+#if 0
+ [ REDIR_INIT_IDER ] = GTK_STOCK_,
+ [ REDIR_RUN_IDER ] = GTK_STOCK_,
+ [ REDIR_CLOSING ] = GTK_STOCK_,
+#endif
+ [ REDIR_CLOSED ] = GTK_STOCK_DISCONNECT,
+ [ REDIR_ERROR ] = GTK_STOCK_DISCONNECT,
+};
+
+static char amt_host[64];
+static char amt_port[16];
+static char amt_user[32] = "admin";
+static char amt_pass[32];
+static int amt_trace;
+static int amt_debug;
+
+static int gamt_getstring(GtkWidget *window, char *title, char *message,
+ char *dest, int dlen, int hide);
+static int gamt_connect(struct gamt_window *gamt);
+static void gamt_rebuild_hosts(struct gamt_window *gamt);
+
+/* ------------------------------------------------------------------ */
+
+#define CFG_SECTION "config", "config"
+#define CFG_FONT CFG_SECTION, "font"
+#define CFG_FOREGROUND CFG_SECTION, "foreground"
+#define CFG_BACKGROUND CFG_SECTION, "background"
+#define CFG_BLINK CFG_SECTION, "blink-cursor"
+
+/* ------------------------------------------------------------------ */
+
+static void menu_cb_connect(GtkAction *action, void *data)
+{
+ struct gamt_window *gamt = data;
+ int rc;
+
+ if (gamt->redir.state != REDIR_NONE &&
+ gamt->redir.state != REDIR_CLOSED &&
+ gamt->redir.state != REDIR_ERROR)
+ /* already have an active connection */
+ return;
+
+ rc = gamt_getstring(gamt->win, "Connecting",
+ "Connect to host ?",
+ amt_host, sizeof(amt_host), 0);
+ if (0 != rc)
+ return;
+
+ gamt_connect(gamt);
+}
+
+static void menu_cb_connect_to(GtkAction *action, void *data)
+{
+ struct gamt_window *gamt = data;
+
+ if (gamt->redir.state != REDIR_NONE &&
+ gamt->redir.state != REDIR_CLOSED &&
+ gamt->redir.state != REDIR_ERROR)
+ /* already have an active connection */
+ return;
+
+ if (1 != sscanf(gtk_action_get_name(action), "ConnectTo_%s", amt_host))
+ return;
+ gamt_connect(gamt);
+}
+
+static void menu_cb_disconnect(GtkAction *action, void *data)
+{
+ struct gamt_window *gamt = data;
+
+ if (gamt->redir.state != REDIR_RUN_SOL)
+ return;
+ redir_sol_stop(&gamt->redir);
+}
+
+static void menu_cb_config_font(GtkAction *action, void *data)
+{
+ struct gamt_window *gamt = data;
+ GtkWidget *dialog;
+ char *fontname;
+
+ dialog = gtk_font_selection_dialog_new("Terminal font");
+ fontname = cfg_get_str(CFG_FONT);
+ gtk_font_selection_dialog_set_font_name
+ (GTK_FONT_SELECTION_DIALOG(dialog), fontname);
+
+ gtk_widget_show_all(dialog);
+ switch (gtk_dialog_run(GTK_DIALOG(dialog))) {
+ case GTK_RESPONSE_OK:
+ fontname = gtk_font_selection_dialog_get_font_name
+ (GTK_FONT_SELECTION_DIALOG(dialog));
+ vte_terminal_set_font_from_string(VTE_TERMINAL(gamt->vte), fontname);
+ cfg_set_str(CFG_FONT, fontname);
+ break;
+ }
+ gtk_widget_destroy(dialog);
+}
+
+static int pickcolor(char *title, GdkColor *color)
+{
+ GtkWidget *dialog;
+ GtkColorSelection *csel;
+ int rc = -1;
+
+ dialog = gtk_color_selection_dialog_new(title);
+ csel = GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(dialog)->colorsel);
+ gtk_color_selection_set_has_opacity_control(csel, FALSE);
+ gtk_color_selection_set_current_color(csel, color);
+
+ gtk_widget_show_all(dialog);
+ switch (gtk_dialog_run(GTK_DIALOG(dialog))) {
+ case GTK_RESPONSE_OK:
+ gtk_color_selection_get_current_color(csel, color);
+ rc = 0;
+ }
+ gtk_widget_destroy(dialog);
+ return rc;
+}
+
+static void menu_cb_config_fg(GtkAction *action, void *data)
+{
+ struct gamt_window *gamt = data;
+ GdkColor color = {0,0,0,0};
+ char name[16];
+
+ gdk_color_parse(cfg_get_str(CFG_FOREGROUND), &color);
+ if (0 != pickcolor("Text color", &color))
+ return;
+ vte_terminal_set_color_foreground(VTE_TERMINAL(gamt->vte), &color);
+ snprintf(name, sizeof(name), "#%04x%04x%04x",
+ color.red, color.green, color.blue);
+ cfg_set_str(CFG_FOREGROUND, name);
+}
+
+static void menu_cb_config_bg(GtkAction *action, void *data)
+{
+ struct gamt_window *gamt = data;
+ GdkColor color = {0,0,0,0};
+ char name[16];
+
+ gdk_color_parse(cfg_get_str(CFG_BACKGROUND), &color);
+ if (0 != pickcolor("Background color", &color))
+ return;
+ vte_terminal_set_color_background(VTE_TERMINAL(gamt->vte), &color);
+ snprintf(name, sizeof(name), "#%04x%04x%04x",
+ color.red, color.green, color.blue);
+ cfg_set_str(CFG_BACKGROUND, name);
+}
+
+static void menu_cb_blink_cursor(GtkToggleAction *action, gpointer user_data)
+{
+ struct gamt_window *gamt = user_data;
+ gboolean state = gtk_toggle_action_get_active(action);
+
+ if (amt_debug)
+ fprintf(stderr, "%s: %s\n", __FUNCTION__, state ? "on" : "off");
+ cfg_set_bool(CFG_BLINK, state);
+ vte_terminal_set_cursor_blinks(VTE_TERMINAL(gamt->vte), state);
+}
+
+static void menu_cb_quit(GtkAction *action, void *data)
+{
+ struct gamt_window *gamt = data;
+
+ gtk_widget_destroy(gamt->win);
+}
+
+static void show_manpage(char *page, char *section)
+{
+ char buf[64];
+
+
+ switch(fork()) {
+ case -1:
+ perror("fork");
+ break;
+ case 0:
+ /* child: try xdg-open first ... */
+ snprintf(buf, sizeof(buf), "man:%s(%s)", page, section);
+ execlp("xdg-open", "xdg-open", buf, NULL);
+ perror("execlp(xdg-open)");
+ /* ... fallback is an xterm with man */
+ snprintf(buf, sizeof(buf), "manual page: %s(%s)", page, section);
+ execlp("xterm", "xterm",
+ "-title", buf,
+ "-e", "man", section, page,
+ NULL);
+ perror("execlp(xterm)");
+ exit(1);
+ break;
+ default:
+ /* parent */
+ break;
+ }
+}
+
+static void menu_cb_man_gamt(GtkAction *action, void *data)
+{
+ show_manpage("gamt", "1");
+}
+
+static void menu_cb_man_amt_howto(GtkAction *action, void *data)
+{
+ show_manpage("amt-howto", "7");
+}
+
+static void menu_cb_about(GtkAction *action, void *data)
+{
+ static char *comments = "Intel AMT serial-over-lan client";
+ static char *copyright = "(c) 2007 Gerd Hoffmann";
+ static char *website = "http://dl.bytesex.org/releases/amtterm/";
+ static char *authors[] = { "Gerd Hoffmann <kraxel@redhat.com>", NULL };
+ struct gamt_window *gamt = data;
+
+ gtk_show_about_dialog(GTK_WINDOW(gamt->win),
+ "authors", authors,
+ "comments", comments,
+ "copyright", copyright,
+ "logo-icon-name", GTK_STOCK_ABOUT,
+ "version", VERSION,
+ "website", website,
+// "license", "GPLv2+",
+ NULL);
+}
+
+static void destroy_cb(GtkWidget *widget, gpointer data)
+{
+ struct gamt_window *gamt = data;
+
+ gtk_main_quit();
+ free(gamt);
+}
+
+/* ------------------------------------------------------------------ */
+
+static int recv_gtk(void *cb_data, unsigned char *buf, int len)
+{
+ struct gamt_window *gamt = cb_data;
+ vte_terminal_feed(VTE_TERMINAL(gamt->vte), buf, len);
+ return 0;
+}
+
+static void state_gtk(void *cb_data, enum redir_state old, enum redir_state new)
+{
+ struct gamt_window *gamt = cb_data;
+ unsigned char buf[128];
+ int last;
+
+ switch (new) {
+ case REDIR_ERROR:
+#if 0
+ snprintf(buf, sizeof(buf), "%s: %s FAILED (%s)", gamt->redir.host,
+ redir_state_desc(old), gamt->redir.err);
+#else
+ snprintf(buf, sizeof(buf), "%s: ERROR: %s", gamt->redir.host,
+ gamt->redir.err);
+#endif
+ if (old == REDIR_AUTH) {
+ /* ask for a new password next time ... */
+ strcpy(amt_pass, "");
+ }
+ break;
+ case REDIR_RUN_SOL:
+ last = cfg_get_int("config", "hosts", gamt->redir.host, 0);
+ cfg_set_int("config", "hosts", gamt->redir.host, time(NULL));
+ if (!last)
+ gamt_rebuild_hosts(gamt);
+ /* fall through */
+ default:
+ snprintf(buf, sizeof(buf), "%s: %s", gamt->redir.host,
+ redir_state_desc(new));
+ break;
+ }
+ if (state_stock[new])
+ gtk_image_set_from_stock(GTK_IMAGE(gamt->icon), state_stock[new],
+ GTK_ICON_SIZE_SMALL_TOOLBAR);
+ gtk_label_set_text(GTK_LABEL(gamt->status), buf);
+}
+
+static void user_input(VteTerminal *vte, gchar *buf, guint len,
+ gpointer data)
+{
+ struct gamt_window *gamt = data;
+
+ if (gamt->redir.state == REDIR_RUN_SOL)
+ redir_sol_send(&gamt->redir, buf, len);
+}
+
+/* ------------------------------------------------------------------ */
+
+static const GtkActionEntry entries[] = {
+ {
+ .name = "FileMenu",
+ .label = "_File",
+ },{
+ .name = "ConfigMenu",
+ .label = "_Options",
+ },{
+ .name = "HostMenu",
+ .label = "Ho_sts",
+ },{
+ .name = "HelpMenu",
+ .label = "_Help",
+ },{
+
+ /* File menu */
+ .name = "Connect",
+ .stock_id = GTK_STOCK_CONNECT,
+ .label = "_Connect ...",
+ .callback = G_CALLBACK(menu_cb_connect),
+ },{
+ .name = "Disconnect",
+ .stock_id = GTK_STOCK_DISCONNECT,
+ .label = "_Disconnect",
+ .callback = G_CALLBACK(menu_cb_disconnect),
+ },{
+ .name = "Quit",
+ .stock_id = GTK_STOCK_QUIT,
+ .label = "_Quit",
+ .callback = G_CALLBACK(menu_cb_quit),
+ },{
+
+ /* Config menu */
+ .name = "VteFont",
+ .stock_id = GTK_STOCK_SELECT_FONT,
+ .label = "Terminal _font ...",
+ .callback = G_CALLBACK(menu_cb_config_font),
+ },{
+ .name = "VteForeground",
+// .stock_id = GTK_STOCK_SELECT_COLOR,
+ .label = "_Text Color ...",
+ .callback = G_CALLBACK(menu_cb_config_fg),
+ },{
+ .name = "VteBackground",
+ .label = "_Background Color ...",
+ .callback = G_CALLBACK(menu_cb_config_bg),
+ },{
+
+ /* Help menu */
+ .name = "ManGamt1",
+ .stock_id = GTK_STOCK_HELP,
+ .label = "Manual Page",
+ .callback = G_CALLBACK(menu_cb_man_gamt),
+ },{
+ .name = "ManAmtHowto7",
+ .stock_id = GTK_STOCK_INFO,
+ .label = "AMT HowTo",
+ .callback = G_CALLBACK(menu_cb_man_amt_howto),
+ },{
+ .name = "About",
+ .stock_id = GTK_STOCK_ABOUT,
+ .label = "_About ...",
+ .callback = G_CALLBACK(menu_cb_about),
+ }
+};
+
+static const GtkToggleActionEntry tentries[] = {
+ {
+ .name = "BlinkCursor",
+ .label = "Blinking cursor",
+ .callback = G_CALLBACK(menu_cb_blink_cursor),
+ }
+};
+
+static char ui_xml[] =
+"<ui>\n"
+" <menubar action='MainMenu'>\n"
+" <menu action='FileMenu'>\n"
+" <menuitem action='Connect'/>\n"
+" <menuitem action='Disconnect'/>\n"
+" <separator/>\n"
+" <menuitem action='Quit'/>\n"
+" </menu>\n"
+" <menu action='HostMenu'>\n"
+" </menu>\n"
+" <menu action='ConfigMenu'>\n"
+" <menuitem action='VteFont'/>\n"
+" <menuitem action='VteForeground'/>\n"
+" <menuitem action='VteBackground'/>\n"
+" <separator/>\n"
+" <menuitem action='BlinkCursor'/>\n"
+" </menu>\n"
+" <menu action='HelpMenu'>\n"
+" <menuitem action='ManGamt1'/>\n"
+" <menuitem action='ManAmtHowto7'/>\n"
+" <separator/>\n"
+" <menuitem action='About'/>\n"
+" </menu>\n"
+" </menubar>\n"
+" <toolbar action='ToolBar'>\n"
+" <toolitem action='Quit'/>\n"
+" <toolitem action='Connect'/>\n"
+" <toolitem action='Disconnect'/>\n"
+" </toolbar>\n"
+"</ui>\n";
+
+static char hosts_xml_start[] =
+"<ui>\n"
+" <menubar name='MainMenu'>\n"
+" <menu action='HostMenu'>\n";
+
+static char hosts_xml_end[] =
+" </menu>\n"
+" </menubar>\n"
+"</ui>\n";
+
+/* ------------------------------------------------------------------ */
+
+static int gamt_getstring(GtkWidget *window, char *title, char *message,
+ char *dest, int dlen, int hide)
+{
+ GtkWidget *dialog, *label, *entry;
+ const char *txt;
+ int retval;
+
+ /* Create the widgets */
+ dialog = gtk_dialog_new_with_buttons(title,
+ GTK_WINDOW(window),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_STOCK_OK,
+ GTK_RESPONSE_ACCEPT,
+ GTK_STOCK_CANCEL,
+ GTK_RESPONSE_REJECT,
+ NULL);
+ gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
+
+ label = gtk_label_new(message);
+ gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
+
+ entry = gtk_entry_new();
+ gtk_entry_set_text(GTK_ENTRY(entry), dest);
+ gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
+ if (hide)
+ gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
+
+ gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), label);
+ gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), entry);
+ gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(dialog)->vbox), 10);
+#if 0 /* FIXME: doesn't work ... */
+ gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), 10);
+#endif
+
+ /* show and wait for response */
+ gtk_widget_show_all(dialog);
+ switch (gtk_dialog_run(GTK_DIALOG(dialog))) {
+ case GTK_RESPONSE_ACCEPT:
+ txt = gtk_entry_get_text(GTK_ENTRY(entry));
+ snprintf(dest, dlen, "%s", txt);
+ retval = 0;
+ break;
+ default:
+ retval = -1;
+ break;
+ }
+ gtk_widget_destroy(dialog);
+ return retval;
+}
+
+static gboolean gamt_data(GIOChannel *source, GIOCondition condition,
+ gpointer data)
+{
+ struct gamt_window *gamt = data;
+
+ redir_data(&gamt->redir);
+
+ if (gamt->redir.state == REDIR_CLOSED ||
+ gamt->redir.state == REDIR_ERROR) {
+ g_source_destroy(g_main_context_find_source_by_id
+ (g_main_context_default(), gamt->id));
+ gamt->id = 0;
+ gamt->ch = NULL;
+ }
+ return TRUE;
+}
+
+static void gamt_rebuild_hosts(struct gamt_window *gamt)
+{
+ int count, size, pos;
+ char *hosts_xml, *host, action[128];
+ GtkActionEntry entry;
+ GError *err = NULL;
+
+ /* remove */
+ if (gamt->hosts_id) {
+ gtk_ui_manager_remove_ui(gamt->ui, gamt->hosts_id);
+ gamt->hosts_id = 0;
+ }
+ if (gamt->hosts_ag) {
+ gtk_ui_manager_remove_action_group(gamt->ui, gamt->hosts_ag);
+ g_object_unref(gamt->hosts_ag);
+ gamt->hosts_ag = NULL;
+ }
+
+ /* build */
+ memset(&entry, 0, sizeof(entry));
+ entry.callback = G_CALLBACK(menu_cb_connect_to);
+ gamt->hosts_ag = gtk_action_group_new("HostActions");
+ count = cfg_entries_count("config", "hosts");
+ size = 128 * count + sizeof(hosts_xml_start) + sizeof(hosts_xml_end);
+ hosts_xml = malloc(size); pos = 0;
+ pos += sprintf(hosts_xml+pos, "%s", hosts_xml_start);
+ for (host = cfg_entries_first("config", "hosts");
+ NULL != host;
+ host = cfg_entries_next("config", "hosts", host)) {
+ snprintf(action, sizeof(action), "ConnectTo_%s", host);
+ pos += snprintf(hosts_xml+pos, 128,
+ " <menuitem action='%s'/>\n",
+ action);
+ entry.name = action;
+ entry.label = host;
+ gtk_action_group_add_actions(gamt->hosts_ag, &entry, 1, gamt);
+ }
+ pos += sprintf(hosts_xml+pos, "%s", hosts_xml_end);
+
+ /* add */
+ gtk_ui_manager_insert_action_group(gamt->ui, gamt->hosts_ag, 1);
+ gamt->hosts_id = gtk_ui_manager_add_ui_from_string(gamt->ui, hosts_xml, -1, &err);
+ if (!gamt->hosts_id) {
+ g_message("building host menu failed: %s", err->message);
+ g_error_free(err);
+ }
+}
+
+static int gamt_connect(struct gamt_window *gamt)
+{
+ int rc;
+
+ if (0 == strlen(amt_pass)) {
+ char msg[128];
+
+ snprintf(msg, sizeof(msg), "AMT password for %s@%s ?",
+ amt_user, amt_host);
+ rc = gamt_getstring(gamt->win, "Authentication", msg,
+ amt_pass, sizeof(amt_pass), 1);
+ if (0 != rc)
+ return -1;
+ }
+
+ memset(&gamt->redir, 0, sizeof(gamt->redir));
+ memcpy(&gamt->redir.type, "SOL ", 4);
+
+ snprintf(gamt->redir.host, sizeof(gamt->redir.host), "%s", amt_host);
+ snprintf(gamt->redir.port, sizeof(gamt->redir.port), "%s", amt_port);
+ snprintf(gamt->redir.user, sizeof(gamt->redir.user), "%s", amt_user);
+ snprintf(gamt->redir.pass, sizeof(gamt->redir.pass), "%s", amt_pass);
+
+ gamt->redir.verbose = 1;
+ gamt->redir.trace = amt_trace;
+ gamt->redir.cb_data = gamt;
+ gamt->redir.cb_recv = recv_gtk;
+ gamt->redir.cb_state = state_gtk;
+
+ if (-1 == redir_connect(&gamt->redir))
+ return -1;
+
+ fcntl(gamt->redir.sock, F_SETFD, FD_CLOEXEC);
+ vte_terminal_reset(VTE_TERMINAL(gamt->vte), TRUE, TRUE);
+ gamt->ch = g_io_channel_unix_new(gamt->redir.sock);
+ gamt->id = g_io_add_watch(gamt->ch, G_IO_IN, gamt_data, gamt);
+ redir_start(&gamt->redir);
+ return 0;
+}
+
+static struct gamt_window *gamt_window()
+{
+ GtkWidget *vbox, *hbox, *frame, *item;
+ GdkColor color;
+ GError *err;
+ gboolean state;
+ struct gamt_window *gamt;
+ char *str;
+
+ gamt = malloc(sizeof(*gamt));
+ if (NULL == gamt)
+ return NULL;
+ memset(gamt,0,sizeof(*gamt));
+
+ /* gtk toplevel */
+ gamt->win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ g_signal_connect(G_OBJECT(gamt->win), "destroy",
+ G_CALLBACK(destroy_cb), gamt);
+
+ /* menu + toolbar */
+ gamt->ui = gtk_ui_manager_new();
+ gamt->ag = gtk_action_group_new("MenuActions");
+ gtk_action_group_add_actions(gamt->ag, entries, G_N_ELEMENTS(entries), gamt);
+ gtk_action_group_add_toggle_actions(gamt->ag, tentries,
+ G_N_ELEMENTS(tentries), gamt);
+ gtk_ui_manager_insert_action_group(gamt->ui, gamt->ag, 0);
+#if 0
+ GtkAccelGroup *accel = gtk_ui_manager_get_accel_group(gamt->ui);
+ gtk_window_add_accel_group(GTK_WINDOW(gamt->win), accel);
+#endif
+
+ err = NULL;
+ if (!gtk_ui_manager_add_ui_from_string(gamt->ui, ui_xml, -1, &err)) {
+ g_message("building menus failed: %s", err->message);
+ g_error_free(err);
+ exit(1);
+ }
+ gamt_rebuild_hosts(gamt);
+
+ /* vte terminal */
+ gamt->vte = vte_terminal_new();
+ g_signal_connect(gamt->vte, "commit", G_CALLBACK(user_input), gamt);
+ vte_terminal_set_scrollback_lines(VTE_TERMINAL(gamt->vte), 4096);
+ str = cfg_get_str(CFG_FONT);
+ vte_terminal_set_font_from_string(VTE_TERMINAL(gamt->vte), str);
+
+ /* FIXME: make configurable */
+ vte_terminal_set_backspace_binding(VTE_TERMINAL(gamt->vte),
+ VTE_ERASE_ASCII_BACKSPACE);
+ vte_terminal_set_delete_binding(VTE_TERMINAL(gamt->vte),
+ VTE_ERASE_AUTO);
+
+ item = gtk_ui_manager_get_widget(gamt->ui, "/MainMenu/ConfigMenu/BlinkCursor");
+ state = cfg_get_bool(CFG_BLINK, 0);
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), state);
+ vte_terminal_set_cursor_blinks(VTE_TERMINAL(gamt->vte), state);
+
+ /* other widgets */
+ gamt->status = gtk_label_new("idle");
+ gtk_misc_set_alignment(GTK_MISC(gamt->status), 0, 0.5);
+ gtk_misc_set_padding(GTK_MISC(gamt->status), 3, 1);
+ gamt->icon = gtk_image_new_from_stock(GTK_STOCK_DISCONNECT,
+ GTK_ICON_SIZE_SMALL_TOOLBAR);
+
+ /* Make a vbox and put stuff in */
+ vbox = gtk_vbox_new(FALSE, 1);
+ hbox = gtk_hbox_new(FALSE, 1);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 1);
+ gtk_container_add(GTK_CONTAINER(gamt->win), vbox);
+ item = gtk_ui_manager_get_widget(gamt->ui, "/MainMenu");
+ gtk_box_pack_start(GTK_BOX(vbox), item, FALSE, FALSE, 0);
+#if 0
+ item = gtk_ui_manager_get_widget(gamt->ui, "/ToolBar");
+ gtk_box_pack_start(GTK_BOX(vbox), item, FALSE, FALSE, 0);
+#endif
+ gtk_box_pack_start(GTK_BOX(vbox), gamt->vte, TRUE, TRUE, 0);
+ gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
+
+ frame = gtk_frame_new(NULL);
+ gtk_container_add(GTK_CONTAINER(frame), gamt->status);
+ gtk_box_pack_start(GTK_BOX(hbox), frame, TRUE, TRUE, 0);
+
+ frame = gtk_frame_new(NULL);
+ gtk_container_add(GTK_CONTAINER(frame), gamt->icon);
+ gtk_box_pack_end(GTK_BOX(hbox), frame, FALSE, TRUE, 0);
+
+ /* display window */
+ gtk_widget_show_all(gamt->win);
+
+ str = cfg_get_str(CFG_FOREGROUND);
+ if (str) {
+ gdk_color_parse(str, &color);
+ vte_terminal_set_color_foreground(VTE_TERMINAL(gamt->vte), &color);
+ }
+ str = cfg_get_str(CFG_BACKGROUND);
+ if (str) {
+ gdk_color_parse(str, &color);
+ vte_terminal_set_color_background(VTE_TERMINAL(gamt->vte), &color);
+ }
+
+ return gamt;
+}
+
+/* ------------------------------------------------------------------ */
+
+static void usage(FILE *fp)
+{
+ fprintf(fp,
+ "\n"
+ "This is " APPNAME ", release " VERSION ", I'll establish\n"
+ "serial-over-lan (sol) connections to your Intel AMT boxes.\n"
+ "\n"
+ "usage: " APPNAME " [options] host\n"
+ "options:\n"
+ " -h print this text\n"
+ " -u user username (default: admin)\n"
+ " -p pass password (default: $AMT_PASSWORD)\n"
+ " -f font terminal font\n"
+ " -c color text color\n"
+ " -b color backgrounf color\n"
+ "\n"
+ "By default port 16994 is used.\n"
+ "If no password is given " APPNAME " will ask for one.\n"
+ "\n"
+ "-- \n"
+ "(c) 2007 Gerd Hoffmann <kraxel@redhat.com>\n"
+ "\n");
+}
+
+int
+main(int argc, char *argv[])
+{
+ Display *dpy;
+ struct gamt_window *gamt;
+ char configfile[256];
+ char *h;
+ int c;
+
+ if (NULL != (h = getenv("AMT_PASSWORD")))
+ snprintf(amt_pass, sizeof(amt_pass), "%s", h);
+
+ /* read config, make sure we have sane defaults */
+ snprintf(configfile, sizeof(configfile), "%s/.gamtrc", getenv("HOME"));
+ cfg_parse_file("config", configfile);
+ if (!cfg_get_str(CFG_FONT))
+ cfg_set_str(CFG_FONT, "monospace 12");
+ if (!cfg_get_str(CFG_FOREGROUND))
+ cfg_set_str(CFG_FOREGROUND, "gray");
+ if (!cfg_get_str(CFG_BACKGROUND))
+ cfg_set_str(CFG_BACKGROUND, "black");
+
+ gtk_init(&argc, &argv);
+ dpy = gdk_x11_display_get_xdisplay(gdk_display_get_default());
+ fcntl(ConnectionNumber(dpy),F_SETFD,FD_CLOEXEC);
+
+ for (;;) {
+ if (-1 == (c = getopt(argc, argv, "hdtu:p:f:c:b:")))
+ break;
+ switch (c) {
+ case 'd':
+ amt_debug++;
+ break;
+ case 't':
+ amt_trace++;
+ break;
+ case 'u':
+ snprintf(amt_user, sizeof(amt_user), "%s", optarg);
+ break;
+ case 'p':
+ snprintf(amt_pass, sizeof(amt_pass), "%s", optarg);
+ memset(optarg,'*',strlen(optarg)); /* rm passwd from ps list */
+ break;
+ case 'f':
+ cfg_set_str(CFG_FONT, optarg);
+ break;
+ case 'c':
+ cfg_set_str(CFG_FOREGROUND, optarg);
+ break;
+ case 'b':
+ cfg_set_str(CFG_BACKGROUND, optarg);
+ break;
+
+ case 'h':
+ usage(stdout);
+ exit(0);
+ default:
+ usage(stderr);
+ exit(1);
+ }
+ }
+
+ gamt = gamt_window();
+ if (NULL == gamt)
+ exit(1);
+
+ if (optind+1 <= argc) {
+ snprintf(amt_host, sizeof(amt_host), "%s", argv[optind]);
+ gamt_connect(gamt);
+ }
+
+ gtk_main();
+ cfg_write_file("config", configfile);
+ exit(0);
+}
--- /dev/null
+[Desktop Entry]
+Encoding=UTF-8
+Name=gamt
+Comment=Intel AMT serial-over-lan client.
+Exec=gamt
+Terminal=false
+Type=Application
+Categories=GTK;System;Network;Utility;
--- /dev/null
+.TH gamt 1 "(c) 2007 Gerd Hoffmann"
+.SH NAME
+gamt - Intel AMT serial-over-lan (sol) client.
+.SH SYNOPSIS
+.B gamt [ options ] [ host ]
+.SH DESCRIPTION
+.B gamt
+provides access to the serial-over-lan port of Intel AMT managed
+machines.
+.B host
+is the hostname or IP address of the machine gamt should connect
+to.
+.P
+For more inforamtions on Intel AMT check amt-howto(7).
+.SH OPTIONS
+.TP
+.B -h
+Display help text.
+.TP
+.B -u <user>
+Specify username, defaults to "admin".
+.TP
+.B -p <pass>
+Specify password.
+.B gamt
+will prompt you if unspecified.
+.TP
+.B -f <font>
+Specify terminal font, defaults to "monospace 12".
+.TP
+.B -c <color>
+Specify terminal text color, defaults to "gray".
+.TP
+.B -b <color>
+Specify terminal background color, defaults to "black".
+.P
+Font, colors can also be changed using menu options. These settings
+are also written to the
+.B ~/.gamtrc
+config file, so they persistent.
+.SH ENVIRONMENT
+.TP
+.B AMT_PASSWORD
+Default value for the password.
+.SH SEE ALSO
+amtterm(1), amttool(1), amt-howto(7)
+.SH AUTHOR
+(c) 2007 Gerd Hoffmann <kraxel@redhat.com>
--- /dev/null
+#ifndef _LIST_H_
+#define _LIST_H_
+/*
+ * Simple doubly linked list implementation.
+ * -- shameless stolen from the linux kernel sources
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+ struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+ struct list_head name = LIST_HEAD_INIT(name)
+
+#define INIT_LIST_HEAD(ptr) do { \
+ (ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static __inline__ void __list_add(struct list_head * new,
+ struct list_head * prev,
+ struct list_head * next)
+{
+ next->prev = new;
+ new->next = next;
+ new->prev = prev;
+ prev->next = new;
+}
+
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static __inline__ void list_add(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head, head->next);
+}
+
+/**
+ * list_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static __inline__ void list_add_tail(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static __inline__ void __list_del(struct list_head * prev,
+ struct list_head * next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty on entry does not return true after this, the entry is in an undefined state.
+ */
+static __inline__ void list_del(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+}
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.
+ */
+static __inline__ void list_del_init(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ INIT_LIST_HEAD(entry);
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+static __inline__ int list_empty(struct list_head *head)
+{
+ return head->next == head;
+}
+
+/**
+ * list_splice - join two lists
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+static __inline__ void list_splice(struct list_head *list, struct list_head *head)
+{
+ struct list_head *first = list->next;
+
+ if (first != list) {
+ struct list_head *last = list->prev;
+ struct list_head *at = head->next;
+
+ first->prev = head;
+ head->next = first;
+
+ last->next = at;
+ at->prev = last;
+ }
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr: the &struct list_head pointer.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+ ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
+
+/**
+ * list_for_each - iterate over a list
+ * @pos: the &struct list_head to use as a loop counter.
+ * @head: the head for your list.
+ */
+#define list_for_each(pos, head) \
+ for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * list_for_each_safe - iterate over a list safe against removal of list entry
+ * @pos: the &struct list_head to use as a loop counter.
+ * @n: another &struct list_head to use as temporary storage
+ * @head: the head for your list.
+ */
+#define list_for_each_safe(pos, n, head) \
+ for (pos = (head)->next, n = pos->next; pos != (head); \
+ pos = n, n = pos->next)
+
+/**
+ * list_for_each_prev - iterate over a list in reverse order
+ * @pos: the &struct list_head to use as a loop counter.
+ * @head: the head for your list.
+ */
+#define list_for_each_prev(pos, head) \
+ for (pos = (head)->prev; pos != (head); pos = pos->prev)
+
+#endif /* _LIST_H_ */
--- /dev/null
+#
+# simple autoconf system for GNU make
+#
+# (c) 2002-2006 Gerd Hoffmann <kraxel@suse.de>
+#
+# credits for creating this one go to the autotools people because
+# they managed it to annoy lots of developers and users (including
+# me) with version incompatibilities.
+#
+# This file is public domain. No warranty. If it breaks you keep
+# both pieces.
+#
+########################################################################
+
+# verbose yes/no
+verbose ?= no
+
+# some stuff used by the tests
+ifneq ($(verbose),no)
+ # verbose (for debug)
+ ac_init = echo "checking $(1) ... " >&2; rc=no
+ ac_b_cmd = echo "run: $(1)" >&2; $(1) >/dev/null && rc=yes
+ ac_s_cmd = echo "run: $(1)" >&2; rc=`$(1)`
+ ac_fini = echo "... result is $${rc}" >&2; echo >&2; echo "$${rc}"
+else
+ # normal
+ ac_init = echo -n "checking $(1) ... " >&2; rc=no
+ ac_b_cmd = $(1) >/dev/null 2>&1 && rc=yes
+ ac_s_cmd = rc=`$(1) 2>/dev/null`
+ ac_fini = echo "$${rc}" >&2; echo "$${rc}"
+endif
+
+# some helpers to build cflags and related variables
+ac_def_cflags_1 = $(if $(filter yes,$($(1))),-D$(1))
+ac_lib_cflags = $(foreach lib,$(1),$(call ac_def_cflags_1,HAVE_LIB$(lib)))
+ac_inc_cflags = $(foreach inc,$(1),$(call ac_def_cflags_1,HAVE_$(inc)))
+ac_lib_mkvar_1 = $(if $(filter yes,$(HAVE_LIB$(1))),$($(1)_$(2)))
+ac_lib_mkvar = $(foreach lib,$(1),$(call ac_lib_mkvar_1,$(lib),$(2)))
+
+
+########################################################################
+# the tests ...
+
+# get uname
+ac_uname = $(shell \
+ $(call ac_init,for system);\
+ $(call ac_s_cmd,uname -s | tr 'A-Z' 'a-z');\
+ $(call ac_fini))
+
+# check for some header file
+# args: header file
+ac_header = $(shell \
+ $(call ac_init,for $(1));\
+ $(call ac_b_cmd,echo '\#include <$(1)>' |\
+ $(CC) $(CFLAGS) -E -);\
+ $(call ac_fini))
+
+# check for some function
+# args: function [, additional libs ]
+ac_func = $(shell \
+ $(call ac_init,for $(1));\
+ echo 'void $(1)(void); int main(void) {$(1)();return 0;}' \
+ > __actest.c;\
+ $(call ac_b_cmd,$(CC) $(CFLAGS) $(LDFLAGS) -o \
+ __actest __actest.c $(2));\
+ rm -f __actest __actest.c;\
+ $(call ac_fini))
+
+# check for some library
+# args: function, library [, additional libs ]
+ac_lib = $(shell \
+ $(call ac_init,for $(1) in $(2));\
+ echo 'void $(1)(void); int main(void) {$(1)();return 0;}' \
+ > __actest.c;\
+ $(call ac_b_cmd,$(CC) $(CFLAGS) $(LDFLAGS) -o \
+ __actest __actest.c -l$(2) $(3));\
+ rm -f __actest __actest.c;\
+ $(call ac_fini))
+
+# check if some compiler flag works
+# args: compiler flag
+ac_cflag = $(shell \
+ $(call ac_init,if $(CC) supports $(1));\
+ echo 'int main() {return 0;}' > __actest.c;\
+ $(call ac_b_cmd,$(CC) $(CFLAGS) $(1) $(LDFLAGS) -o \
+ __actest __actest.c);\
+ rm -f __actest __actest.c;\
+ $(call ac_fini))
+
+# check for some binary
+# args: binary name
+ac_binary = $(shell \
+ $(call ac_init,for $(1));\
+ $(call ac_s_cmd,which $(1));\
+ bin="$$rc";rc="no";\
+ $(call ac_b_cmd,test -x "$$$$bin");\
+ $(call ac_fini))
+
+# check if lib64 is used
+ac_lib64 = $(shell \
+ $(call ac_init,for libdir name);\
+ $(call ac_s_cmd,$(CC) -print-search-dirs | grep -q lib64 &&\
+ echo "lib64" || echo "lib");\
+ $(call ac_fini))
+
+# check for x11 ressource dir prefix
+ac_resdir = $(shell \
+ $(call ac_init,for X11 app-defaults prefix);\
+ $(call ac_s_cmd, for dir in \
+ /etc/X11/app-defaults \
+ /usr/X11R6/lib/X11/app-defaults \
+ /usr/share/X11/app-defaults \
+ /usr/lib/X11/app-defaults \
+ ; do test -d "$$dir" || continue;\
+ dirname "$$dir"; break; done);\
+ $(call ac_fini))
+
+# check if package is installed, via pkg-config
+# args: pkg name
+ac_pkg_config = $(shell \
+ $(call ac_init,for $(1) (using pkg-config));\
+ $(call ac_b_cmd, pkg-config $(1));\
+ $(call ac_fini))
+
+
+########################################################################
+# build Make.config
+
+define newline
+
+
+endef
+make-config-q = $(subst $(newline),\n,$(make-config))
+
+ifeq ($(filter config,$(MAKECMDGOALS)),config)
+.PHONY: Make.config
+ LIB := $(call ac_lib64)
+else
+ LIB ?= $(call ac_lib64)
+ LIB := $(LIB)
+endif
+.PHONY: config
+config: Make.config
+ @true
+
+Make.config: $(srcdir)/GNUmakefile
+ @echo -e "$(make-config-q)" > $@
+ @echo
+ @echo "Make.config written, edit if needed"
+ @echo
--- /dev/null
+#
+# some rules to compile stuff ...
+#
+# (c) 2002-2006 Gerd Hoffmann <kraxel@suse.de>
+#
+# main features:
+# * autodependencies via "cpp -MD"
+# * fancy, non-verbose output
+#
+# This file is public domain. No warranty. If it breaks you keep
+# both pieces.
+#
+########################################################################
+
+# verbose yes/no
+verbose ?= no
+
+# dependency files
+tmpdep = mk/$(subst /,_,$*).tmp
+depfile = mk/$(subst /,_,$*).dep
+depfiles = mk/*.dep
+
+compile_c = $(CC) $(CFLAGS) -Wp,-MD,$(tmpdep) -c -o $@ $<
+compile_cc = $(CXX) $(CXXFLAGS) -Wp,-MD,$(tmpdep) -c -o $@ $<
+fixup_deps = sed -e "s|.*\.o:|$@:|" < $(tmpdep) > $(depfile) && rm -f $(tmpdep)
+cc_makedirs = mkdir -p $(dir $@) $(dir $(depfile))
+
+link_app = $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS)
+link_so = $(CC) $(LDFLAGS) -shared -Wl,-soname,$(@F) -o $@ $^ $(LDLIBS)
+ar_lib = rm -f $@ && ar -r $@ $^ && ranlib $@
+
+moc_h = $(MOC) $< -o $@
+msgfmt_po = msgfmt -o $@ $<
+
+# non-verbose output
+ifeq ($(verbose),no)
+ echo_compile_c = echo " CC " $@
+ echo_compile_cc = echo " CXX " $@
+ echo_link_app = echo " LD " $@
+ echo_link_so = echo " LD " $@
+ echo_ar_lib = echo " AR " $@
+ echo_moc_h = echo " MOC " $@
+ echo_msgfmt_po = echo " MSGFMT " $@
+else
+ echo_compile_c = echo $(compile_c)
+ echo_compile_cc = echo $(compile_cc)
+ echo_link_app = echo $(link_app)
+ echo_link_so = echo $(link_so)
+ echo_ar_lib = echo $(ar_lib)
+ echo_moc_h = echo $(moc_h)
+ echo_msgfmt_po = echo $(msgfmt_po)
+endif
+
+%.o: %.c
+ @$(cc_makedirs)
+ @$(echo_compile_c)
+ @$(compile_c)
+ @$(fixup_deps)
+
+%.o: %.cc
+ @$(cc_makedirs)
+ @$(echo_compile_cc)
+ @$(compile_cc)
+ @$(fixup_deps)
+
+%.o: %.cpp
+ @$(cc_makedirs)
+ @$(echo_compile_cc)
+ @$(compile_cc)
+ @$(fixup_deps)
+
+
+%.so: %.o
+ @$(echo_link_so)
+ @$(link_so)
+
+%: %.o
+ @$(echo_link_app)
+ @$(link_app)
+
+%.moc : %.h
+ @$(echo_moc_h)
+ @$(moc_h)
+
+%.mo : %.po
+ @$(echo_msgfmt_po)
+ @$(msgfmt_po)
+
--- /dev/null
+# just some maintainer stuff for me ...
+########################################################################
+
+make-sync-dir = $(HOME)/projects/gnu-makefiles
+
+.PHONY: sync
+sync:: distclean
+ test -d $(make-sync-dir)
+ rm -f $(srcdir)/INSTALL $(srcdir)/mk/*.mk
+ cp -v $(make-sync-dir)/INSTALL $(srcdir)/.
+ cp -v $(make-sync-dir)/*.mk $(srcdir)/mk
+ chmod 444 $(srcdir)/INSTALL $(srcdir)/mk/*.mk
+
+
+repository = $(shell cat CVS/Repository)
+release-dir ?= $(HOME)/projects/Releases
+release-pub ?= goldbach@me.in-berlin.de:dl.bytesex.org/releases/$(repository)
+tarball = $(release-dir)/$(repository)-$(VERSION).tar.gz
+
+.PHONY: release
+release:
+ cvs tag $(RELTAG)
+ cvs export -r $(RELTAG) -d "$(repository)-$(VERSION)" "$(repository)"
+ find "$(repository)-$(VERSION)" -name .cvsignore -exec rm -fv "{}" ";"
+ tar -c -z -f "$(tarball)" "$(repository)-$(VERSION)"
+ rm -rf "$(repository)-$(VERSION)"
+ scp $(tarball) $(release-pub)
+
--- /dev/null
+# common variables ...
+########################################################################
+
+# directories
+DESTDIR =
+srcdir ?= .
+prefix ?= /usr/local
+bindir = $(DESTDIR)$(prefix)/bin
+libdir = $(DESTDIR)$(prefix)/$(LIB)
+shrdir = $(DESTDIR)$(prefix)/share
+mandir = $(shrdir)/man
+locdir = $(shrdir)/locale
+appdir = $(shrdir)/applications
+
+# package + version
+empty :=
+space := $(empty) $(empty)
+ifneq ($(wildcard $(srcdir)/VERSION),)
+ VERSION := $(shell cat $(srcdir)/VERSION)
+else
+ VERSION := 42
+endif
+RELTAG := v$(subst .,_,$(VERSION))
+
+# programs
+CC ?= gcc
+CXX ?= g++
+MOC ?= $(if $(QTDIR),$(QTDIR)/bin/moc,moc)
+
+STRIP ?= -s
+INSTALL ?= install
+INSTALL_BINARY := $(INSTALL) $(STRIP)
+INSTALL_SCRIPT := $(INSTALL)
+INSTALL_DATA := $(INSTALL) -m 644
+INSTALL_DIR := $(INSTALL) -d
+
+# cflags
+CFLAGS ?= -g -O2
+CXXFLAGS ?= $(CFLAGS)
+CFLAGS += -Wall -Wmissing-prototypes -Wstrict-prototypes \
+ -Wpointer-arith -Wunused
+CXXFLAGS += -Wall -Wpointer-arith -Wunused
+
+# add /usr/local to the search path if something is in there ...
+ifneq ($(wildcard /usr/local/include/*.h),)
+ CFLAGS += -I/usr/local/include
+ LDFLAGS += -L/usr/local/$(LIB)
+endif
+
+# fixup include path for $(srcdir) != "."
+ifneq ($(srcdir),.)
+ CFLAGS += -I. -I$(srcdir)
+endif
+
--- /dev/null
+/*
+ * functions to read and write config files
+ *
+ * Copyright (C) 2007 Gerd Hoffmann <kraxel@redhat.com
+ *
+ * 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.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "list.h"
+#include "parseconfig.h"
+
+struct cfg_entry {
+ struct list_head next;
+ char *name;
+ unsigned int flags;
+ char *value;
+};
+
+struct cfg_section {
+ struct list_head next;
+ char *name;
+ unsigned int flags;
+ struct list_head entries;
+};
+
+struct cfg_domain {
+ struct list_head next;
+ char *name;
+ struct list_head sections;
+};
+
+LIST_HEAD(domains);
+
+/* ------------------------------------------------------------------------ */
+/* internal stuff */
+
+static struct cfg_domain *d_last;
+static struct cfg_section *s_last;
+static struct cfg_entry *e_last;
+
+static struct cfg_domain*
+cfg_find_domain(char *dname)
+{
+ struct list_head *item;
+ struct cfg_domain *domain;
+
+ if (d_last && 0 == strcmp(d_last->name,dname))
+ return d_last;
+ d_last = NULL;
+ s_last = NULL;
+ e_last = NULL;
+
+ list_for_each(item,&domains) {
+ domain = list_entry(item, struct cfg_domain, next);
+ if (0 == strcasecmp(domain->name,dname)) {
+ d_last = domain;
+ return domain;
+ }
+ }
+ return NULL;
+}
+
+static struct cfg_domain*
+cfg_get_domain(char *dname)
+{
+ struct cfg_domain *domain;
+
+ domain = cfg_find_domain(dname);
+ if (NULL == domain) {
+ domain = malloc(sizeof(*domain));
+ memset(domain,0,sizeof(*domain));
+ domain->name = strdup(dname);
+ INIT_LIST_HEAD(&domain->sections);
+ list_add_tail(&domain->next,&domains);
+ }
+ d_last = domain;
+ return domain;
+}
+
+static struct cfg_section*
+cfg_find_section(struct cfg_domain *domain, char *sname)
+{
+ struct list_head *item;
+ struct cfg_section *section;
+
+ if (s_last && 0 == strcmp(s_last->name,sname))
+ return s_last;
+ s_last = NULL;
+ e_last = NULL;
+
+ list_for_each(item,&domain->sections) {
+ section = list_entry(item, struct cfg_section, next);
+ if (0 == strcasecmp(section->name,sname)) {
+ s_last = section;
+ return section;
+ }
+ }
+ return NULL;
+}
+
+static struct cfg_section*
+cfg_get_section(struct cfg_domain *domain, char *sname)
+{
+ struct cfg_section *section;
+
+ section = cfg_find_section(domain,sname);
+ if (NULL == section) {
+ section = malloc(sizeof(*section));
+ memset(section,0,sizeof(*section));
+ section->name = strdup(sname);
+ INIT_LIST_HEAD(§ion->entries);
+ list_add_tail(§ion->next,&domain->sections);
+ }
+ s_last = section;
+ return section;
+}
+
+static struct cfg_entry*
+cfg_find_entry(struct cfg_section *section, char *ename)
+{
+ struct list_head *item;
+ struct cfg_entry *entry;
+
+ if (e_last && 0 == strcmp(e_last->name,ename))
+ return e_last;
+ e_last = NULL;
+
+ list_for_each(item,§ion->entries) {
+ entry = list_entry(item, struct cfg_entry, next);
+ if (0 == strcasecmp(entry->name,ename)) {
+ e_last = entry;
+ return entry;
+ }
+ }
+ return NULL;
+}
+
+static struct cfg_entry*
+cfg_get_entry(struct cfg_section *section, char *ename)
+{
+ struct cfg_entry *entry;
+
+ entry = cfg_find_entry(section,ename);
+ if (NULL == entry) {
+ entry = malloc(sizeof(*entry));
+ memset(entry,0,sizeof(*entry));
+ entry->name = strdup(ename);
+ list_add_tail(&entry->next,§ion->entries);
+ }
+ e_last = entry;
+ return entry;
+}
+
+static void
+cfg_set_entry(struct cfg_section *section, char *name, const char *value)
+{
+ struct cfg_entry *entry;
+
+ entry = cfg_get_entry(section,name);
+ if (entry->value)
+ free(entry->value);
+ entry->value = strdup(value);
+}
+
+static struct cfg_section*
+cfg_get_sec(char *dname, char *sname)
+{
+ struct cfg_domain *domain;
+
+ domain = cfg_find_domain(dname);
+ if (NULL == domain)
+ return NULL;
+ return cfg_find_section(domain,sname);
+}
+
+static struct cfg_entry*
+cfg_get_ent(char *dname, char *sname, char *ename)
+{
+ struct cfg_section *section;
+
+ section = cfg_get_sec(dname,sname);
+ if (NULL == section)
+ return NULL;
+ return cfg_find_entry(section,ename);
+}
+
+/* ------------------------------------------------------------------------ */
+/* import / add / del config data */
+
+int
+cfg_parse_file(char *dname, char *filename)
+{
+ struct cfg_domain *domain = NULL;
+ struct cfg_section *section = NULL;
+ char line[256],tag[64],value[192];
+ FILE *fp;
+ int nr;
+
+ if (NULL == (fp = fopen(filename,"r")))
+ return -1;
+
+ nr = 0;
+ domain = cfg_get_domain(dname);
+ while (NULL != fgets(line,255,fp)) {
+ nr++;
+ if (1 == sscanf(line,"# include \"%[^\"]\"",value)) {
+ /* includes */
+ char *h,*inc;
+ inc = malloc(strlen(filename)+strlen(value));
+ strcpy(inc,filename);
+ h = strrchr(inc,'/');
+ if (h)
+ h++;
+ else
+ h = inc;
+ strcpy(h,value);
+ cfg_parse_file(dname,inc);
+ free(inc);
+ continue;
+ }
+ if (line[0] == '\n' || line[0] == '#' || line[0] == '%')
+ continue;
+ if (1 == sscanf(line,"[%99[^]]]",value)) {
+ /* [section] */
+ section = cfg_get_section(domain,value);
+ } else if (2 == sscanf(line," %63[^= ] = %191[^\n]",tag,value)) {
+ /* foo = bar */
+ if (NULL == section) {
+ fprintf(stderr,"%s:%d: error: no section\n",filename,nr);
+ } else {
+ char *c = value + strlen(value)-1;
+ while (c > value && (*c == ' ' || *c == '\t'))
+ *(c--) = 0;
+ cfg_set_entry(section,tag,value);
+ }
+ } else {
+ /* Huh ? */
+ fprintf(stderr,"%s:%d: syntax error\n",filename,nr);
+ }
+ }
+ fclose(fp);
+ return 0;
+}
+
+void
+cfg_set_str(char *dname, char *sname, char *ename, const char *value)
+{
+ struct cfg_domain *domain = NULL;
+ struct cfg_section *section = NULL;
+
+ if (NULL == value) {
+ cfg_del_entry(dname, sname, ename);
+ } else {
+ domain = cfg_get_domain(dname);
+ section = cfg_get_section(domain,sname);
+ cfg_set_entry(section,ename,value);
+ }
+}
+
+void
+cfg_set_int(char *dname, char *sname, char *ename, int value)
+{
+ char str[32];
+
+ snprintf(str,sizeof(str),"%d",value);
+ cfg_set_str(dname,sname,ename,str);
+}
+
+void
+cfg_set_bool(char *dname, char *sname, char *ename, int value)
+{
+ cfg_set_str(dname,sname,ename, value ? "true" : "false");
+}
+
+void
+cfg_del_section(char *dname, char *sname)
+{
+ struct cfg_section *section;
+ struct cfg_entry *entry;
+
+ section= cfg_get_sec(dname,sname);
+ if (!section)
+ return;
+ list_del(§ion->next);
+ while (!list_empty(§ion->entries)) {
+ entry = list_entry(section->entries.next, struct cfg_entry, next);
+ list_del(&entry->next);
+ free(entry->name);
+ free(entry->value);
+ free(entry);
+ }
+ s_last = NULL;
+ e_last = NULL;
+ free(section->name);
+ free(section);
+}
+
+void
+cfg_del_entry(char *dname, char *sname, char *ename)
+{
+ struct cfg_entry *entry;
+
+ entry = cfg_get_ent(dname,sname,ename);
+ if (!entry)
+ return;
+ e_last = NULL;
+ list_del(&entry->next);
+ free(entry->name);
+ free(entry->value);
+ free(entry);
+}
+
+void
+cfg_parse_cmdline(int *argc, char **argv, struct cfg_cmdline *opt)
+{
+ int i,j,o,shift,len;
+ char sopt,*lopt;
+
+ for (i = 1; i < *argc;) {
+ if (argv[i][0] != '-') {
+ i++;
+ continue;
+ }
+ if (argv[i][1] == 0) {
+ i++;
+ continue;
+ }
+
+ sopt = 0;
+ lopt = NULL;
+ if (argv[i][1] != '-' &&
+ argv[i][2] == 0) {
+ /* short option: -f */
+ sopt = argv[i][1];
+ }
+ if (argv[i][1] != '-') {
+ /* long option: -foo */
+ lopt = argv[i]+1;
+ } else {
+ /* also accept gnu-style: --foo */
+ lopt = argv[i]+2;
+ }
+
+ for (shift = 0, o = 0;
+ 0 == shift && opt[o].cmdline != NULL;
+ o++) {
+ len = strlen(opt[o].cmdline);
+
+ if (opt[o].yesno && sopt && sopt == opt[o].letter) {
+ /* yesno: -f */
+ cfg_set_bool(opt[o].option.domain,
+ opt[o].option.section,
+ opt[o].option.entry,
+ 1);
+ shift = 1;
+
+ } else if (opt[o].needsarg && sopt && sopt == opt[o].letter &&
+ i+1 < *argc) {
+ /* arg: -f bar */
+ cfg_set_str(opt[o].option.domain,
+ opt[o].option.section,
+ opt[o].option.entry,
+ argv[i+1]);
+ shift = 2;
+
+ } else if (opt[o].value && sopt && sopt == opt[o].letter) {
+ /* -f sets fixed value */
+ cfg_set_str(opt[o].option.domain,
+ opt[o].option.section,
+ opt[o].option.entry,
+ opt[o].value);
+ shift = 1;
+
+ } else if (opt[o].yesno && lopt &&
+ 0 == strcmp(lopt,opt[o].cmdline)) {
+ /* yesno: -foo */
+ cfg_set_bool(opt[o].option.domain,
+ opt[o].option.section,
+ opt[o].option.entry,
+ 1);
+ shift = 1;
+
+ } else if (opt[o].yesno && lopt &&
+ 0 == strncmp(lopt,"no",2) &&
+ 0 == strcmp(lopt+2,opt[o].cmdline)) {
+ /* yesno: -nofoo */
+ cfg_set_bool(opt[o].option.domain,
+ opt[o].option.section,
+ opt[o].option.entry,
+ 0);
+ shift = 1;
+
+ } else if (opt[o].needsarg && lopt &&
+ 0 == strcmp(lopt,opt[o].cmdline) &&
+ i+1 < *argc) {
+ /* arg: -foo bar */
+ cfg_set_str(opt[o].option.domain,
+ opt[o].option.section,
+ opt[o].option.entry,
+ argv[i+1]);
+ shift = 2;
+
+ } else if (opt[o].needsarg && lopt &&
+ 0 == strncmp(lopt,opt[o].cmdline,len) &&
+ 0 == strncmp(lopt+len,"=",1)) {
+ /* arg: -foo=bar */
+ cfg_set_str(opt[o].option.domain,
+ opt[o].option.section,
+ opt[o].option.entry,
+ argv[i]+2+len);
+ shift = 1;
+
+ } else if (opt[o].value && lopt &&
+ 0 == strcmp(lopt,opt[o].cmdline)) {
+ /* -foo sets some fixed value */
+ cfg_set_str(opt[o].option.domain,
+ opt[o].option.section,
+ opt[o].option.entry,
+ opt[o].value);
+ shift = 1;
+ }
+ }
+
+ if (shift) {
+ /* remove processed args */
+ for (j = i; j < *argc+1-shift; j++)
+ argv[j] = argv[j+shift];
+ (*argc) -= shift;
+ } else
+ i++;
+ }
+}
+
+void
+cfg_help_cmdline(FILE *fp, struct cfg_cmdline *opt, int w1, int w2, int w3)
+{
+ char *val;
+ int o,len;
+
+ for (o = 0; opt[o].cmdline != NULL; o++) {
+ fprintf(fp,"%*s",w1,"");
+ if (opt[o].letter) {
+ fprintf(fp,"-%c ",opt[o].letter);
+ } else {
+ fprintf(fp," ");
+ }
+
+ if (opt[o].yesno) {
+ len = fprintf(fp,"-(no)%s ",opt[o].cmdline);
+ } else if (opt[o].needsarg) {
+ len = fprintf(fp,"-%s <arg> ",opt[o].cmdline);
+ } else {
+ len = fprintf(fp,"-%s ",opt[o].cmdline);
+ }
+ if (len < w2)
+ fprintf(fp,"%*s",w2-len,"");
+
+ len = fprintf(fp,"%s ",opt[o].desc);
+
+ if (w3) {
+ if (len < w3)
+ fprintf(fp,"%*s",w3-len,"");
+ val = cfg_get_str(opt[o].option.domain,
+ opt[o].option.section,
+ opt[o].option.entry);
+ if (val)
+ fprintf(fp,"[%s]",val);
+ }
+ fprintf(fp,"\n");
+ }
+}
+
+/* ------------------------------------------------------------------------ */
+/* export config data */
+
+static int cfg_mkdir(char *filename)
+{
+ char *h;
+ int rc;
+
+ h = strrchr(filename,'/');
+ if (!h || h == filename)
+ return -1;
+ *h = '\0';
+ rc = mkdir(filename,0777);
+ if (-1 == rc && ENOENT == errno) {
+ cfg_mkdir(filename);
+ rc = mkdir(filename,0777);
+ }
+ if (-1 == rc)
+ fprintf(stderr,"mkdir(%s): %s\n",filename,strerror(errno));
+ *h = '/';
+ return rc;
+}
+
+int
+cfg_write_file(char *dname, char *filename)
+{
+ struct list_head *item1,*item2;
+ struct cfg_domain *domain;
+ struct cfg_section *section;
+ struct cfg_entry *entry;
+ char *bfile, *tfile;
+ int len;
+ FILE *fp;
+
+ len = strlen(filename)+10;
+ bfile = malloc(len);
+ tfile = malloc(len);
+ sprintf(bfile,"%s~",filename);
+ sprintf(tfile,"%s.$$$",filename);
+
+ fp = fopen(tfile,"w");
+ if (NULL == fp && ENOENT == errno) {
+ cfg_mkdir(tfile);
+ fp = fopen(tfile,"w");
+ }
+ if (NULL == fp) {
+ fprintf(stderr,"open(%s): %s\n",tfile,strerror(errno));
+ return -1;
+ }
+
+ domain = cfg_find_domain(dname);
+ if (NULL != domain) {
+ list_for_each(item1,&domain->sections) {
+ section = list_entry(item1, struct cfg_section, next);
+ fprintf(fp,"[%s]\n",section->name);
+ list_for_each(item2,§ion->entries) {
+ entry = list_entry(item2, struct cfg_entry, next);
+ fprintf(fp,"%s = %s\n",entry->name,entry->value);
+ }
+ fprintf(fp,"\n");
+ }
+ }
+ fclose(fp);
+
+ if (-1 == unlink(bfile) && ENOENT != errno) {
+ fprintf(stderr,"unlink(%s): %s\n",bfile,strerror(errno));
+ return -1;
+ }
+ if (-1 == rename(filename,bfile) && ENOENT != errno) {
+ fprintf(stderr,"rename(%s,%s): %s\n",filename,bfile,strerror(errno));
+ return -1;
+ }
+ if (-1 == rename(tfile,filename)) {
+ fprintf(stderr,"rename(%s,%s): %s\n",tfile,filename,strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+/* ------------------------------------------------------------------------ */
+/* list / search config data */
+
+char*
+cfg_sections_first(char *dname)
+{
+ struct list_head *item;
+ struct cfg_domain *domain;
+ struct cfg_section *section;
+
+ domain = cfg_find_domain(dname);
+ if (NULL == domain)
+ return NULL;
+
+ item = &domain->sections;
+ if (item->next == &domain->sections)
+ return NULL;
+ section = list_entry(item->next, struct cfg_section, next);
+ s_last = section;
+ e_last = NULL;
+ return section->name;
+}
+
+char*
+cfg_sections_next(char *dname, char *current)
+{
+ struct list_head *item;
+ struct cfg_domain *domain;
+ struct cfg_section *section;
+
+ domain = cfg_find_domain(dname);
+ if (NULL == domain)
+ return NULL;
+ section = cfg_find_section(domain,current);
+ if (NULL == section)
+ return NULL;
+ item = §ion->next;
+
+ if (item->next == &domain->sections)
+ return NULL;
+ section = list_entry(item->next, struct cfg_section, next);
+ s_last = section;
+ e_last = NULL;
+ return section->name;
+}
+
+char*
+cfg_sections_prev(char *dname, char *current)
+{
+ struct list_head *item;
+ struct cfg_domain *domain;
+ struct cfg_section *section;
+
+ domain = cfg_find_domain(dname);
+ if (NULL == domain)
+ return NULL;
+ section = cfg_find_section(domain,current);
+ if (NULL == section)
+ return NULL;
+ item = §ion->next;
+
+ if (item->prev == &domain->sections)
+ return NULL;
+ section = list_entry(item->prev, struct cfg_section, next);
+ s_last = section;
+ e_last = NULL;
+ return section->name;
+}
+
+unsigned int cfg_sections_count(char *dname)
+{
+ struct list_head *item;
+ struct cfg_domain *domain;
+ int count = 0;
+
+ domain = cfg_find_domain(dname);
+ if (NULL != domain)
+ list_for_each(item,&domain->sections)
+ count++;
+ return count;
+}
+
+char* cfg_sections_index(char *dname, int i)
+{
+ struct list_head *item;
+ struct cfg_domain *domain;
+ struct cfg_section *section;
+ int count = 0;
+
+ domain = cfg_find_domain(dname);
+ if (NULL == domain)
+ return NULL;
+
+ list_for_each(item,&domain->sections) {
+ if (i == count) {
+ section = list_entry(item, struct cfg_section, next);
+ s_last = section;
+ e_last = NULL;
+ return section->name;
+ }
+ count++;
+ }
+ return NULL;
+}
+
+char*
+cfg_entries_first(char *dname, char *sname)
+{
+ struct list_head *item;
+ struct cfg_section *section;
+ struct cfg_entry *entry;
+
+ section = cfg_get_sec(dname,sname);
+ if (NULL == section)
+ return NULL;
+
+ item = §ion->entries;
+ if (item->next == §ion->entries)
+ return NULL;
+ entry = list_entry(item->next, struct cfg_entry, next);
+ e_last = entry;
+ return entry->name;
+}
+
+char*
+cfg_entries_next(char *dname, char *sname, char *current)
+{
+ struct list_head *item;
+ struct cfg_section *section;
+ struct cfg_entry *entry;
+
+ section = cfg_get_sec(dname,sname);
+ if (NULL == section)
+ return NULL;
+ entry = cfg_find_entry(section,current);
+ if (NULL == entry)
+ return NULL;
+ item = &entry->next;
+
+ if (item->next == §ion->entries)
+ return NULL;
+ entry = list_entry(item->next, struct cfg_entry, next);
+ e_last = entry;
+ return entry->name;
+}
+
+char*
+cfg_entries_prev(char *dname, char *sname, char *current)
+{
+ struct list_head *item;
+ struct cfg_section *section;
+ struct cfg_entry *entry;
+
+ section = cfg_get_sec(dname,sname);
+ if (NULL == section)
+ return NULL;
+ entry = cfg_find_entry(section,current);
+ if (NULL == entry)
+ return NULL;
+ item = &entry->next;
+
+ if (item->prev == §ion->entries)
+ return NULL;
+ entry = list_entry(item->prev, struct cfg_entry, next);
+ e_last = entry;
+ return entry->name;
+}
+
+unsigned int cfg_entries_count(char *dname, char *sname)
+{
+ struct list_head *item;
+ struct cfg_section *section;
+ int count = 0;
+
+ section = cfg_get_sec(dname,sname);
+ if (NULL != section)
+ list_for_each(item,§ion->entries)
+ count++;
+ return count;
+}
+
+char* cfg_entries_index(char *dname, char *sname, int i)
+{
+ struct list_head *item;
+ struct cfg_section *section;
+ struct cfg_entry *entry;
+ int count = 0;
+
+ section = cfg_get_sec(dname,sname);
+ if (NULL == section)
+ return NULL;
+
+ list_for_each(item,§ion->entries) {
+ if (i == count) {
+ entry = list_entry(item, struct cfg_entry, next);
+ e_last = entry;
+ return entry->name;
+ }
+ count++;
+ }
+ return NULL;
+}
+
+char* cfg_search(char *dname, char *sname, char *ename, char *value)
+{
+ struct list_head *item1,*item2;
+ struct cfg_domain *domain;
+ struct cfg_section *section;
+ struct cfg_entry *entry;
+
+ domain = cfg_find_domain(dname);
+ if (NULL == domain)
+ return NULL;
+ list_for_each(item1,&domain->sections) {
+ section = list_entry(item1, struct cfg_section, next);
+ if (sname && 0 != strcasecmp(section->name,sname))
+ continue;
+ if (!ename)
+ return section->name;
+ list_for_each(item2,§ion->entries) {
+ entry = list_entry(item2, struct cfg_entry, next);
+ if (0 != strcasecmp(entry->name,ename))
+ continue;
+ if (0 == strcasecmp(entry->value,value))
+ return section->name;
+ }
+ }
+ return NULL;
+}
+
+/* ------------------------------------------------------------------------ */
+/* get config data */
+
+char*
+cfg_get_str(char *dname, char *sname, char *ename)
+{
+ struct cfg_entry *entry;
+
+ entry = cfg_get_ent(dname, sname, ename);
+ if (NULL == entry)
+ return NULL;
+ return entry->value;
+}
+
+unsigned int
+cfg_get_int(char *dname, char *sname, char *ename, unsigned int def)
+{
+ char *val;
+
+ val = cfg_get_str(dname,sname,ename);
+ if (NULL == val)
+ return def;
+ return atoi(val);
+}
+
+signed int
+cfg_get_signed_int(char *dname, char *sname, char *ename, signed int def)
+{
+ char *val;
+
+ val = cfg_get_str(dname,sname,ename);
+ if (NULL == val)
+ return def;
+ return atoi(val);
+}
+
+float
+cfg_get_float(char *dname, char *sname, char *ename, float def)
+{
+ char *val;
+
+ val = cfg_get_str(dname,sname,ename);
+ if (NULL == val)
+ return def;
+ return atof(val);
+}
+
+int
+cfg_get_bool(char *dname, char *sname, char *ename, int def)
+{
+ static char *yes[] = { "true", "yes", "on", "1" };
+ char *val;
+ int i;
+ int retval = 0;
+
+ val = cfg_get_str(dname,sname,ename);
+ if (NULL == val)
+ return def;
+ for (i = 0; i < sizeof(yes)/sizeof(yes[0]); i++)
+ if (0 == strcasecmp(val,yes[i]))
+ retval = 1;
+ return retval;
+}
+
+/* ------------------------------------------------------------------------ */
+/* get/set flags */
+
+unsigned int cfg_get_sflags(char *dname, char *sname)
+{
+ struct cfg_section *section;
+
+ section = cfg_get_sec(dname, sname);
+ if (NULL == section)
+ return 0;
+ return section->flags;
+}
+
+unsigned int cfg_get_eflags(char *dname, char *sname, char *ename)
+{
+ struct cfg_entry *entry;
+
+ entry = cfg_get_ent(dname, sname, ename);
+ if (NULL == entry)
+ return 0;
+ return entry->flags;
+}
+
+unsigned int cfg_set_sflags(char *dname, char *sname,
+ unsigned int mask, unsigned int bits)
+{
+ struct cfg_section *section;
+
+ section = cfg_get_sec(dname, sname);
+ if (NULL == section)
+ return 0;
+ section->flags &= ~mask;
+ section->flags |= bits;
+ return section->flags;
+}
+
+unsigned int cfg_set_eflags(char *dname, char *sname, char *ename,
+ unsigned int mask, unsigned int bits)
+{
+ struct cfg_entry *entry;
+
+ entry = cfg_get_ent(dname, sname, ename);
+ if (NULL == entry)
+ return 0;
+ entry->flags &= ~mask;
+ entry->flags |= bits;
+ return entry->flags;
+}
--- /dev/null
+/* config options */
+struct cfg_option {
+ char *domain;
+ char *section;
+ char *entry;
+};
+struct cfg_cmdline {
+ char letter;
+ char *cmdline;
+ struct cfg_option option;
+ char *value;
+ char *desc;
+ int needsarg:1;
+ int yesno:1;
+};
+void cfg_parse_cmdline(int *argc, char **argv, struct cfg_cmdline *opt);
+void cfg_help_cmdline(FILE *fp, struct cfg_cmdline *opt, int w1, int w2, int w3);
+
+/* file I/O */
+int cfg_parse_file(char *dname, char *filename);
+int cfg_write_file(char *dname, char *filename);
+
+/* update */
+void cfg_set_str(char *dname, char *sname, char *ename,
+ const char *value);
+void cfg_set_int(char *dname, char *sname, char *ename, int value);
+void cfg_set_bool(char *dname, char *sname, char *ename, int value);
+
+void cfg_del_section(char *dname, char *sname);
+void cfg_del_entry(char *dname, char *sname, char *ename);
+
+/* search */
+char* cfg_sections_first(char *dname);
+char* cfg_sections_next(char *dname, char *current);
+char* cfg_sections_prev(char *dname, char *current);
+char* cfg_sections_index(char *dname, int i);
+unsigned int cfg_sections_count(char *dname);
+
+char* cfg_entries_first(char *dname, char *sname);
+char* cfg_entries_next(char *dname, char *sname, char *current);
+char* cfg_entries_prev(char *dname, char *sname, char *current);
+char* cfg_entries_index(char *dname, char *sname, int i);
+unsigned int cfg_entries_count(char *dname, char *sname);
+
+#define cfg_sections_for_each(dname, item) \
+ for (item = cfg_sections_first(dname); NULL != item; \
+ item = cfg_sections_next(dname,item))
+
+char* cfg_search(char *dname, char *sname, char *ename, char *value);
+
+/* read */
+char* cfg_get_str(char *dname, char *sname, char *ename);
+unsigned int cfg_get_int(char *dname, char *sname,
+ char *ename, unsigned int def);
+signed int cfg_get_signed_int(char *dname, char *sname,
+ char *ename, signed int def);
+float cfg_get_float(char *dname, char *sname, char *ename, float def);
+int cfg_get_bool(char *dname, char *sname, char *ename, int def);
+
+unsigned int cfg_get_sflags(char *dname, char *sname);
+unsigned int cfg_get_eflags(char *dname, char *sname, char *ename);
+unsigned int cfg_set_sflags(char *dname, char *sname,
+ unsigned int mask, unsigned int bits);
+unsigned int cfg_set_eflags(char *dname, char *sname, char *ename,
+ unsigned int mask, unsigned int bits);
--- /dev/null
+/*
+ * Intel AMT tcp redirection protocol helper functions.
+ *
+ * Copyright (C) 2007 Gerd Hoffmann <kraxel@redhat.com
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "tcp.h"
+#include "redir.h"
+
+static const char *state_name[] = {
+ [ REDIR_NONE ] = "NONE",
+ [ REDIR_CONNECT ] = "CONNECT",
+ [ REDIR_INIT ] = "INIT",
+ [ REDIR_AUTH ] = "AUTH",
+ [ REDIR_INIT_SOL ] = "INIT_SOL",
+ [ REDIR_RUN_SOL ] = "RUN_SOL",
+ [ REDIR_INIT_IDER ] = "INIT_IDER",
+ [ REDIR_RUN_IDER ] = "RUN_IDER",
+ [ REDIR_CLOSING ] = "CLOSING",
+ [ REDIR_CLOSED ] = "CLOSED",
+ [ REDIR_ERROR ] = "ERROR",
+};
+
+static const char *state_desc[] = {
+ [ REDIR_NONE ] = "disconnected",
+ [ REDIR_CONNECT ] = "connection to host",
+ [ REDIR_INIT ] = "redirection initialization",
+ [ REDIR_AUTH ] = "session authentication",
+ [ REDIR_INIT_SOL ] = "serial-over-lan initialization",
+ [ REDIR_RUN_SOL ] = "serial-over-lan active",
+ [ REDIR_INIT_IDER ] = "IDE redirect initialization",
+ [ REDIR_RUN_IDER ] = "IDE redirect active",
+ [ REDIR_CLOSING ] = "redirection shutdown",
+ [ REDIR_CLOSED ] = "connection closed",
+ [ REDIR_ERROR ] = "failure",
+};
+
+/* ------------------------------------------------------------------ */
+
+static void hexdump(const char *prefix, const unsigned char *data, size_t size)
+{
+ char ascii[17];
+ int i;
+
+ for (i = 0; i < size; i++) {
+ if (0 == (i%16)) {
+ fprintf(stderr,"%s%s%04x:",
+ prefix ? prefix : "",
+ prefix ? ": " : "",
+ i);
+ memset(ascii,0,sizeof(ascii));
+ }
+ if (0 == (i%4))
+ fprintf(stderr," ");
+ fprintf(stderr," %02x",data[i]);
+ ascii[i%16] = isprint(data[i]) ? data[i] : '.';
+ if (15 == (i%16))
+ fprintf(stderr," %s\n",ascii);
+ }
+ if (0 != (i%16)) {
+ while (0 != (i%16)) {
+ if (0 == (i%4))
+ fprintf(stderr," ");
+ fprintf(stderr," ");
+ i++;
+ };
+ fprintf(stderr," %s\n",ascii);
+ }
+}
+
+static ssize_t redir_write(struct redir *r, const char *buf, size_t count)
+{
+ int rc;
+
+ if (r->trace)
+ hexdump("out", buf, count);
+ rc = write(r->sock, buf, count);
+ if (-1 == rc)
+ snprintf(r->err, sizeof(r->err), "write(socket): %s", strerror(errno));
+ return rc;
+}
+
+static void redir_state(struct redir *r, enum redir_state new)
+{
+ enum redir_state old = r->state;
+
+ r->state = new;
+ if (r->cb_state)
+ r->cb_state(r->cb_data, old, new);
+}
+
+/* ------------------------------------------------------------------ */
+
+const char *redir_state_name(enum redir_state state)
+{
+ const char *name = NULL;
+
+ if (state < sizeof(state_name)/sizeof(state_name[0]))
+ name = state_name[state];
+ if (NULL == name)
+ name = "unknown";
+ return name;
+}
+
+const char *redir_state_desc(enum redir_state state)
+{
+ const char *desc = NULL;
+
+ if (state < sizeof(state_desc)/sizeof(state_desc[0]))
+ desc = state_desc[state];
+ if (NULL == desc)
+ desc = "unknown";
+ return desc;
+}
+
+int redir_connect(struct redir *r)
+{
+ static unsigned char *defport = "16994";
+ struct addrinfo ai;
+
+ memset(&ai, 0, sizeof(ai));
+ ai.ai_socktype = SOCK_STREAM;
+ ai.ai_family = PF_UNSPEC;
+ tcp_verbose = r->verbose;
+ redir_state(r, REDIR_CONNECT);
+ r->sock = tcp_connect(&ai, NULL, NULL, r->host,
+ strlen(r->port) ? r->port : defport);
+ if (-1 == r->sock) {
+ redir_state(r, REDIR_ERROR);
+ return -1;
+ }
+ return 0;
+}
+
+int redir_start(struct redir *r)
+{
+ unsigned char request[START_REDIRECTION_SESSION_LENGTH] = {
+ START_REDIRECTION_SESSION, 0, 0, 0, 0, 0, 0, 0
+ };
+
+ memcpy(request+4, r->type, 4);
+ redir_state(r, REDIR_INIT);
+ return redir_write(r, request, sizeof(request));
+}
+
+int redir_stop(struct redir *r)
+{
+ unsigned char request[END_REDIRECTION_SESSION_LENGTH] = {
+ END_REDIRECTION_SESSION, 0, 0, 0
+ };
+
+ redir_state(r, REDIR_CLOSED);
+ redir_write(r, request, sizeof(request));
+ close(r->sock);
+ return 0;
+}
+
+int redir_auth(struct redir *r)
+{
+ int ulen = strlen(r->user);
+ int plen = strlen(r->pass);
+ int len = 11+ulen+plen;
+ int rc;
+ unsigned char *request = malloc(len);
+
+ memset(request, 0, len);
+ request[0] = AUTHENTICATE_SESSION;
+ request[4] = 0x01;
+ request[5] = ulen+plen+2;
+ request[9] = ulen;
+ memcpy(request + 10, r->user, ulen);
+ request[10 + ulen] = plen;
+ memcpy(request + 11 + ulen, r->pass, plen);
+ redir_state(r, REDIR_AUTH);
+ rc = redir_write(r, request, len);
+ free(request);
+ return rc;
+}
+
+int redir_sol_start(struct redir *r)
+{
+ unsigned char request[START_SOL_REDIRECTION_LENGTH] = {
+ START_SOL_REDIRECTION, 0, 0, 0,
+ 0, 0, 0, 0,
+ MAX_TRANSMIT_BUFFER & 0xff,
+ MAX_TRANSMIT_BUFFER >> 8,
+ TRANSMIT_BUFFER_TIMEOUT & 0xff,
+ TRANSMIT_BUFFER_TIMEOUT >> 8,
+ TRANSMIT_OVERFLOW_TIMEOUT & 0xff, TRANSMIT_OVERFLOW_TIMEOUT >> 8,
+ HOST_SESSION_RX_TIMEOUT & 0xff,
+ HOST_SESSION_RX_TIMEOUT >> 8,
+ HOST_FIFO_RX_FLUSH_TIMEOUT & 0xff,
+ HOST_FIFO_RX_FLUSH_TIMEOUT >> 8,
+ HEARTBEAT_INTERVAL & 0xff,
+ HEARTBEAT_INTERVAL >> 8,
+ 0, 0, 0, 0
+ };
+
+ redir_state(r, REDIR_INIT_SOL);
+ return redir_write(r, request, sizeof(request));
+}
+
+int redir_sol_stop(struct redir *r)
+{
+ unsigned char request[END_SOL_REDIRECTION_LENGTH] = {
+ END_SOL_REDIRECTION, 0, 0, 0,
+ 0, 0, 0, 0,
+ };
+
+ redir_state(r, REDIR_CLOSING);
+ return redir_write(r, request, sizeof(request));
+}
+
+int redir_sol_send(struct redir *r, unsigned char *buf, int blen)
+{
+ int len = 10+blen;
+ int rc;
+ unsigned char *request = malloc(len);
+
+ memset(request, 0, len);
+ request[0] = SOL_DATA_TO_HOST;
+ request[8] = blen & 0xff;
+ request[9] = blen >> 8;
+ memcpy(request + 10, buf, blen);
+ rc = redir_write(r, request, len);
+ free(request);
+ return rc;
+}
+
+int redir_sol_recv(struct redir *r)
+{
+ unsigned char msg[64];
+ int count, len, bshift;
+
+ len = r->buf[8] + (r->buf[9] << 8);
+ count = r->blen - 10;
+ if (count > len)
+ count = len;
+ bshift = count + 10;
+ if (r->cb_recv)
+ r->cb_recv(r->cb_data, r->buf + 10, count);
+ len -= count;
+
+ while (len) {
+ if (r->trace)
+ fprintf(stderr, "in+: need %d more data bytes\n", len);
+ count = sizeof(msg);
+ if (count > len)
+ count = len;
+ count = read(r->sock, msg, count);
+ switch (count) {
+ case -1:
+ snprintf(r->err, sizeof(r->err), "read(socket): %s", strerror(errno));
+ return -1;
+ case 0:
+ snprintf(r->err, sizeof(r->err), "EOF from socket");
+ return -1;
+ default:
+ if (r->trace)
+ hexdump("in+", msg, count);
+ if (r->cb_recv)
+ r->cb_recv(r->cb_data, msg, count);
+ len -= count;
+ }
+ }
+
+ return bshift;
+}
+
+int redir_data(struct redir *r)
+{
+ int rc, bshift;
+
+ if (r->trace) {
+ fprintf(stderr, "in --\n");
+ if (r->blen)
+ fprintf(stderr, "in : already have %d\n", r->blen);
+ }
+ rc = read(r->sock, r->buf + r->blen, sizeof(r->buf) - r->blen);
+ switch (rc) {
+ case -1:
+ snprintf(r->err, sizeof(r->err), "read(socket): %s", strerror(errno));
+ goto err;
+ case 0:
+ snprintf(r->err, sizeof(r->err), "EOF from socket");
+ goto err;
+ default:
+ if (r->trace)
+ hexdump("in ", r->buf + r->blen, rc);
+ r->blen += rc;
+ }
+
+ for (;;) {
+ if (r->blen < 4)
+ goto again;
+ bshift = 0;
+
+ switch (r->buf[0]) {
+ case START_REDIRECTION_SESSION_REPLY:
+ bshift = START_REDIRECTION_SESSION_REPLY_LENGTH;
+ if (r->blen < bshift)
+ goto again;
+ if (r->buf[1] != STATUS_SUCCESS) {
+ snprintf(r->err, sizeof(r->err), "redirection session start failed");
+ goto err;
+ }
+ if (-1 == redir_auth(r))
+ goto err;
+ break;
+ case AUTHENTICATE_SESSION_REPLY:
+ bshift = r->blen; /* FIXME */
+ if (r->blen < bshift)
+ goto again;
+ if (r->buf[1] != STATUS_SUCCESS) {
+ snprintf(r->err, sizeof(r->err), "session authentication failed");
+ goto err;
+ }
+ if (-1 == redir_sol_start(r))
+ goto err;
+ break;
+ case START_SOL_REDIRECTION_REPLY:
+ bshift = r->blen; /* FIXME */
+ if (r->blen < bshift)
+ goto again;
+ if (r->buf[1] != STATUS_SUCCESS) {
+ snprintf(r->err, sizeof(r->err), "serial-over-lan redirection failed");
+ goto err;
+ }
+ redir_state(r, REDIR_RUN_SOL);
+ break;
+ case SOL_HEARTBEAT:
+ case SOL_KEEP_ALIVE_PING:
+ case IDER_HEARTBEAT:
+ case IDER_KEEP_ALIVE_PING:
+ bshift = HEARTBEAT_LENGTH;
+ if (r->blen < bshift)
+ goto again;
+ if (HEARTBEAT_LENGTH != redir_write(r, r->buf, HEARTBEAT_LENGTH))
+ goto err;
+ break;
+ case SOL_DATA_FROM_HOST:
+ if (r->blen < 10) /* header length */
+ goto again;
+ bshift = redir_sol_recv(r);
+ if (bshift < 0)
+ goto err;
+ break;
+ case END_SOL_REDIRECTION_REPLY:
+ bshift = r->blen; /* FIXME */
+ if (r->blen < bshift)
+ goto again;
+ redir_stop(r);
+ break;
+ default:
+ snprintf(r->err, sizeof(r->err), "%s: unknown r->buf 0x%02x",
+ __FUNCTION__, r->buf[0]);
+ goto err;
+ }
+
+ if (bshift == r->blen) {
+ r->blen = 0;
+ break;
+ }
+
+ /* have more data, shift by bshift */
+ if (r->trace)
+ fprintf(stderr, "in : shift by %d\n", bshift);
+ memmove(r->buf, r->buf + bshift, r->blen - bshift);
+ r->blen -= bshift;
+ }
+ return 0;
+
+again:
+ /* need more data, jump back into poll/select loop */
+ if (r->trace)
+ fprintf(stderr, "in : need more data\n");
+ return 0;
+
+err:
+ if (r->trace)
+ fprintf(stderr, "in : ERROR (%s)\n", r->err);
+ redir_state(r, REDIR_ERROR);
+ close(r->sock);
+ return -1;
+}
--- /dev/null
+#include "RedirectionConstants.h"
+
+enum redir_state {
+ REDIR_NONE = 0,
+ REDIR_CONNECT = 1,
+ REDIR_INIT = 2,
+ REDIR_AUTH = 3,
+ REDIR_INIT_SOL = 10,
+ REDIR_RUN_SOL = 11,
+ REDIR_INIT_IDER = 20,
+ REDIR_RUN_IDER = 21,
+ REDIR_CLOSING = 30,
+ REDIR_CLOSED = 31,
+ REDIR_ERROR = 40,
+};
+
+struct redir {
+ /* host connection */
+ unsigned char host[64];
+ unsigned char port[16];
+ unsigned char user[16];
+ unsigned char pass[16];
+
+ /* serial-over-lan */
+ unsigned char type[4];
+ int verbose;
+ int trace;
+ enum redir_state state;
+ unsigned char err[128]; // state == REDIR_ERROR
+
+ int sock;
+ unsigned char buf[64];
+ unsigned int blen;
+
+ /* callbacks */
+ void *cb_data;
+ void (*cb_state)(void *cb_data, enum redir_state old, enum redir_state new);
+ int (*cb_recv)(void *cb_data, unsigned char *buf, int len);
+};
+
+const char *redir_state_name(enum redir_state state);
+const char *redir_state_desc(enum redir_state state);
+
+int redir_connect(struct redir *r);
+int redir_start(struct redir *r);
+int redir_stop(struct redir *r);
+int redir_auth(struct redir *r);
+int redir_sol_start(struct redir *r);
+int redir_sol_stop(struct redir *r);
+int redir_sol_send(struct redir *r, unsigned char *buf, int blen);
+int redir_sol_recv(struct redir *r);
+int redir_data(struct redir *r);
--- /dev/null
+/*
+ * TCP helper functions.
+ *
+ * Copyright (C) 2007 Gerd Hoffmann <kraxel@redhat.com
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "tcp.h"
+
+int tcp_verbose;
+
+/* ------------------------------------------------------------------ */
+
+static char *strfamily(int family)
+{
+ switch (family) {
+ case PF_INET6: return "ipv6";
+ case PF_INET: return "ipv4";
+ case PF_UNIX: return "unix";
+ }
+ return "????";
+}
+
+int tcp_connect(struct addrinfo *ai,
+ char *addr, char *port,
+ char *host, char *serv)
+{
+ struct addrinfo *res,*e;
+ struct addrinfo *lres, ask;
+ char uaddr[INET6_ADDRSTRLEN+1];
+ char uport[33];
+ char uhost[INET6_ADDRSTRLEN+1];
+ char userv[33];
+ int sock,rc,opt=1;
+
+ /* lookup peer */
+ ai->ai_flags = AI_CANONNAME;
+ if (0 != (rc = getaddrinfo(host, serv, ai, &res))) {
+ if (tcp_verbose)
+ fprintf(stderr,"getaddrinfo (peer): %s\n", gai_strerror(rc));
+ return -1;
+ }
+ for (e = res; e != NULL; e = e->ai_next) {
+ if (0 != getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen,
+ uhost,INET6_ADDRSTRLEN,userv,32,
+ NI_NUMERICHOST | NI_NUMERICSERV)) {
+ if (tcp_verbose)
+ fprintf(stderr,"getnameinfo (peer): oops\n");
+ continue;
+ }
+ if (-1 == (sock = socket(e->ai_family, e->ai_socktype,
+ e->ai_protocol))) {
+ if (tcp_verbose)
+ fprintf(stderr,"socket (%s): %s\n",
+ strfamily(e->ai_family),strerror(errno));
+ continue;
+ }
+ setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
+ if (NULL != addr || NULL != port) {
+ /* bind local port */
+ memset(&ask,0,sizeof(ask));
+ ask.ai_flags = AI_PASSIVE;
+ ask.ai_family = e->ai_family;
+ ask.ai_socktype = e->ai_socktype;
+ if (0 != (rc = getaddrinfo(addr, port, &ask, &lres))) {
+ if (tcp_verbose)
+ fprintf(stderr,"getaddrinfo (local): %s\n",
+ gai_strerror(rc));
+ continue;
+ }
+ if (0 != getnameinfo((struct sockaddr*)lres->ai_addr,
+ lres->ai_addrlen,
+ uaddr,INET6_ADDRSTRLEN,uport,32,
+ NI_NUMERICHOST | NI_NUMERICSERV)) {
+ if (tcp_verbose)
+ fprintf(stderr,"getnameinfo (local): oops\n");
+ continue;
+ }
+ if (-1 == bind(sock, lres->ai_addr, lres->ai_addrlen)) {
+ if (tcp_verbose)
+ fprintf(stderr,"%s [%s] %s bind: %s\n",
+ strfamily(lres->ai_family),uaddr,uport,
+ strerror(errno));
+ continue;
+ }
+ }
+ /* connect to peer */
+ if (-1 == connect(sock,e->ai_addr,e->ai_addrlen)) {
+ if (tcp_verbose)
+ fprintf(stderr,"%s %s [%s] %s connect: %s\n",
+ strfamily(e->ai_family),e->ai_canonname,uhost,userv,
+ strerror(errno));
+ close(sock);
+ continue;
+ }
+ if (tcp_verbose)
+ fprintf(stderr,"%s %s [%s] %s open\n",
+ strfamily(e->ai_family),e->ai_canonname,uhost,userv);
+ fcntl(sock,F_SETFL,O_NONBLOCK);
+ return sock;
+ }
+ return -1;
+}
+
+int tcp_listen(struct addrinfo *ai, char *addr, char *port)
+{
+ struct addrinfo *res,*e;
+ char uaddr[INET6_ADDRSTRLEN+1];
+ char uport[33];
+ int slisten,rc,opt=1;
+
+ /* lookup */
+ ai->ai_flags = AI_PASSIVE;
+ if (0 != (rc = getaddrinfo(addr, port, ai, &res))) {
+ if (tcp_verbose)
+ fprintf(stderr,"getaddrinfo: %s\n",gai_strerror(rc));
+ exit(1);
+ }
+
+ /* create socket + bind */
+ for (e = res; e != NULL; e = e->ai_next) {
+ getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen,
+ uaddr,INET6_ADDRSTRLEN,uport,32,
+ NI_NUMERICHOST | NI_NUMERICSERV);
+ if (-1 == (slisten = socket(e->ai_family, e->ai_socktype,
+ e->ai_protocol))) {
+ if (tcp_verbose)
+ fprintf(stderr,"socket (%s): %s\n",
+ strfamily(e->ai_family),strerror(errno));
+ continue;
+ }
+ opt = 1;
+ setsockopt(slisten,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
+ if (-1 == bind(slisten, e->ai_addr, e->ai_addrlen)) {
+ if (tcp_verbose)
+ fprintf(stderr,"%s [%s] %s bind: %s\n",
+ strfamily(e->ai_family),uaddr,uport,
+ strerror(errno));
+ continue;
+ }
+ listen(slisten,1);
+ break;
+ }
+ if (NULL == e)
+ return -1;
+
+ /* wait for a incoming connection */
+ if (tcp_verbose)
+ fprintf(stderr,"listen on %s [%s] %s ...\n",
+ strfamily(e->ai_family),uaddr,uport);
+ fcntl(slisten,F_SETFL,O_NONBLOCK);
+ return slisten;
+}
--- /dev/null
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+extern int tcp_verbose;
+
+int tcp_connect(struct addrinfo *ai,
+ char *addr, char *port,
+ char *host, char *serv);
+
+int tcp_listen(struct addrinfo *ai, char *addr, char *port);