Friday, November 27, 2015
How to read Complicated C Language Declaration
There is a simple way to decode complicated C language declaration called The "Clockwise/Spiral Rule". The technique basically starts in the unknown variable/function and move "right" spiral (outward) to decode the variable/function exact type. The details are explained at http://c-faq.com/decl/spiral.anderson.html. It's the simples rule I've ever encountered in reading complicated C language declaration.
Saturday, November 21, 2015
Autotools Conditional Makefile Creation via AM_COND_IF
There are times when you need to generate several Makefiles in one platform but want to prevent generating the same Makefile in another platform, or to generate different Makefile for the latter platform. This is where AM_COND_IF (http://www.gnu.org/s/automake/manual/html_node/Usage-of-Conditionals.html) comes to the rescue.
As cool AM_COND_IF sounds, it takes a bit of exercise to make it work as you intend due to lack of documentation. At least for those not savvy enough with m4 macro language. Now, let's get down to business. These are the rules:
That's it. Hopefully, this helps those playing around with using AM_COND_IF. The key takeaway is: never ever call AC_CONFIG_FILES with the same target Makefile output twice! Even from inside AM_COND_IF. Autotools will complain if you do so and you won't be able to generate the Makefile via autoreconf. You must invent a way to make AC_CONFIG_FILES conform to this rule.
As cool AM_COND_IF sounds, it takes a bit of exercise to make it work as you intend due to lack of documentation. At least for those not savvy enough with m4 macro language. Now, let's get down to business. These are the rules:
- AM_COND_IF cannot be invoked twice with the same Makefile output (assuming you're using AC_CONFIG_FILES with AM_COND_IF).
- You need to create automake conditionals first before using AM_COND_IF
# Platform specific checks libevent_test_on_linux="no" # For host type checks AC_CANONICAL_HOST # OS-specific tests case "${host_os}" in *linux*) # Define we are on Linux AC_DEFINE(HAVE_LINUX, 1, [Current OS is Linux]) libevent_test_on_linux="yes" ;; esac AM_CONDITIONAL(ON_LINUX, test "x$libevent_test_on_linux" = "xyes") # Generate Makefile based on current OS AC_CONFIG_FILES([Makefile lib1/Makefile lib2/Makefile experiment_2/Makefile]) AM_COND_IF([ON_LINUX], [AC_CONFIG_FILES([linux_specific_lib/Makefile])])As you can see, the first invocation of AC_CONFIG_FILES instructs automake to generate Makefile for used by all build platforms. The second invocation of AC_CONFIG_FILES (inside AM_COND_IF), only generate Makefile if the target operating system is Linux. If, for example, you want to support other operating system via a different set of OS-specific Makefiles, you can just copy the Linux implementation and add it to configure.ac, modify the Linux implementation to suit your need.
That's it. Hopefully, this helps those playing around with using AM_COND_IF. The key takeaway is: never ever call AC_CONFIG_FILES with the same target Makefile output twice! Even from inside AM_COND_IF. Autotools will complain if you do so and you won't be able to generate the Makefile via autoreconf. You must invent a way to make AC_CONFIG_FILES conform to this rule.
Friday, November 20, 2015
Openssh Hiccups and Fix on IBM System i (AS/400)
Let me start with the symptoms:
As you can see in the log, nothing particularly revealing. You need to run it in real time to see the connection immediately disconnected (starting at debug1: client_input_channel_req: channel 0 rtype exit-signal reply 0). This makes it clear that the client has successfully authenticated but somehow the shell on the server "died" or some permission problem on the user default login directory or other kinds of permission problem. I have triple checked the permission on all related paths without clear leads.
The next step I did was to check which shell executable is the default shell, i.e. which shell is invoked by openssh when you finished logging in to the machine running openssh. In System i version 6.1 (its AS400 PASE), openssh configuration file is located in QOpenSys/QIBM/UserData/SC1/OpenSSH/openssh-3.8.1p1/etc/sshd_config. Unfortunately, there is no default shell variable set in there. However, I have other System i machine that runs openssh with all default settings just fine. Cross-checking the latter machine I found out the default shell is QOpenSys/usr/bin/bsh. Therefore, I need to check whether bsh is working fine in the problematic System i machine.
I found out the bsh executable in the problematic System i machine is somehow broken. When I run bsh from PASE shell via 5250 terminal (via CALL QP2TERM command), bsh immediately stopped. Basically, the shell said bsh is "killed". I tried other shell, i.e. csh (QOpenSys/usr/bin/csh) and this shell worked. Therefore, what I need to fix the problem is a way to force openssh to use csh as the login shell.
Now, we arrived to the FIX. Forcing openssh to use certain shell when logging in via openssh in System i (at least in version 6.1) can be done via the ibmpaseforishell "magic" keyword (see: http://www-01.ibm.com/support/docview.wss?uid=nas8N1011555). These are the steps:
NOTE:
----------
- It seems that not all System i machine supports ibmpaseforishell keyword. It seems that the machine has to have this PTF. However, the keyword works in the problematic System i machine that I worked with.
- Logging in to the AS/400 (PASE) via openssh always failed despite the username, password and all directory/file permission has been triple-checked and confirmed to be OK.
- From SSH log, it seems that the login is successful but the connection immediately "kicked out" for some reason.
- Run ssh client with most verbose flag.
- Run the shell (default shell) invoked by sshd in the server upon login.
- Look for clues from 1 and 2.
- Fix the problem based on the clue.
debug3: no such identity: /home/pinczakko/.ssh/id_ecdsa: No such file or directory debug1: Trying private key: /home/pinczakko/.ssh/id_ed25519 debug3: no such identity: /home/pinczakko/.ssh/id_ed25519: No such file or directory debug2: we did not send a packet, disable method debug3: authmethod_lookup keyboard-interactive debug3: remaining preferred: password debug3: authmethod_is_enabled keyboard-interactive debug1: Next authentication method: keyboard-interactive debug2: userauth_kbdint debug2: we sent a keyboard-interactive packet, wait for reply debug1: Authentications that can continue: publickey,password,keyboard-interactive debug3: userauth_kbdint: disable: no info_req_seen debug2: we did not send a packet, disable method debug3: authmethod_lookup password debug3: remaining preferred: debug3: authmethod_is_enabled password debug1: Next authentication method: password xxx@10.10.10.10's password: debug2: we sent a password packet, wait for reply debug1: Authentication succeeded (password). Authenticated to 10.10.10.10 ([10.10.10.10]:22). debug1: channel 0: new [client-session] debug3: ssh_session2_open: channel_new: 0 debug2: channel 0: send open debug1: Entering interactive session. debug2: callback start debug2: fd 3 setting TCP_NODELAY debug3: ssh_packet_set_tos: set IP_TOS 0x08 debug2: client_session2_setup: id 0 debug2: channel 0: request shell confirm 1 debug2: callback done debug2: channel 0: open confirm rwindow 0 rmax 32768 debug2: channel 0: rcvd adjust 2097152 debug2: channel_input_status_confirm: type 99 id 0 debug2: shell request accepted on channel 0 debug1: client_input_channel_req: channel 0 rtype exit-signal reply 0 debug2: channel 0: rcvd eof debug2: channel 0: output open -> drain debug2: channel 0: obuf empty debug2: channel 0: close_write debug2: channel 0: output drain -> closed debug2: channel 0: rcvd close debug2: channel 0: close_read debug2: channel 0: input open -> closed debug3: channel 0: will not send data after close debug2: channel 0: almost dead debug2: channel 0: gc: notify user debug2: channel 0: gc: user detached debug2: channel 0: send close debug2: channel 0: is dead debug2: channel 0: garbage collecting debug1: channel 0: free: client-session, nchannels 1 debug3: channel 0: status: The following connections are open: #0 client-session (t4 r0 i3/0 o3/0 fd -1/-1 cc -1) Transferred: sent 3328, received 2568 bytes, in 0.5 seconds Bytes per second: sent 6346.9, received 4897.5 debug1: Exit status -1
As you can see in the log, nothing particularly revealing. You need to run it in real time to see the connection immediately disconnected (starting at debug1: client_input_channel_req: channel 0 rtype exit-signal reply 0). This makes it clear that the client has successfully authenticated but somehow the shell on the server "died" or some permission problem on the user default login directory or other kinds of permission problem. I have triple checked the permission on all related paths without clear leads.
The next step I did was to check which shell executable is the default shell, i.e. which shell is invoked by openssh when you finished logging in to the machine running openssh. In System i version 6.1 (its AS400 PASE), openssh configuration file is located in QOpenSys/QIBM/UserData/SC1/OpenSSH/openssh-3.8.1p1/etc/sshd_config. Unfortunately, there is no default shell variable set in there. However, I have other System i machine that runs openssh with all default settings just fine. Cross-checking the latter machine I found out the default shell is QOpenSys/usr/bin/bsh. Therefore, I need to check whether bsh is working fine in the problematic System i machine.
I found out the bsh executable in the problematic System i machine is somehow broken. When I run bsh from PASE shell via 5250 terminal (via CALL QP2TERM command), bsh immediately stopped. Basically, the shell said bsh is "killed". I tried other shell, i.e. csh (QOpenSys/usr/bin/csh) and this shell worked. Therefore, what I need to fix the problem is a way to force openssh to use csh as the login shell.
- Login to the System i machine via 5250 terminal application.
- Open QOpenSys/QIBM/UserData/SC1/OpenSSH/openssh-3.8.1p1/etc/sshd_config via EDTF. This is the command: EDTF 'QOpenSys/QIBM/UserData/SC1/OpenSSH/openssh-3.8.1p1/etc/sshd_config'
- Add the ibmpaseforishell "magic" keyword near the end of the configuration file. This is the result for me:
#no default banner path #Banner /some/path #ibm pase for IBM i shell ibmpaseforishell /QOpenSys/usr/bin/csh #override default of no subsystems Subsystem sftp /QOpenSys/QIBM/ProdData/SC1/OpenSSH/openssh-3.8.1p1/libexec/sftp-server
As you can see, I changed the default shell to csh via the ibmpaseforishell keyword. - Check that ssh client can now connect to the ssh server (daemon) in AS400. When I carried out this step, I finally get a working shell, i.e. the csh shell.
NOTE:
----------
- It seems that not all System i machine supports ibmpaseforishell keyword. It seems that the machine has to have this PTF. However, the keyword works in the problematic System i machine that I worked with.
Thursday, November 5, 2015
Supporting Out-Of-Source-Code-Tree Build with Autotools
Some opensource code are not trivial to be build out of it's source (code) tree. This is especially true in some opensource libraries because they generate intermediate file(s) which must be handled accordingly. But, fear not, there are two Autoconf constructs (or rather internal variables) that can help you tame this wild library code. They are $(top_srcdir) and $(top_builddir). Refer to Autoconf Preset Output Variables for their details.
Let's assume, your source tree looks like below and your application links to this particular libevent version statically:
.
├── libevent-2.0.22-stable
│ ├── autom4te.cache
│ ├── compat
│ │ └── sys
│ ├── include
│ │ └── event2
│ ├── m4
│ ├── sample
│ ├── test
│ └── WIN32-Code
│ └── event2
└── your_application_code_dir
In your_application_code_dir, you need to have a Makefile.am file with the following contents:
Hopefully this helps those who intend to always build autotools code out-of-(source)-tree.
I'll take libevent as a real-world example because this library generates an intermediate header file at build time which must be included in the build process (event-config.h). This is where $(top_srcdir) and $(top_builddir) come into play. If you want to be able to build out-of-source-tree, you need to include this generated file into your application code that uses libevent. You use $(top_builddir) for that. In the meantime, you also need to include the "ordinary" include file in the source tree, and that's where $(top_srcdir) comes into play.
Let's assume, your source tree looks like below and your application links to this particular libevent version statically:
.
├── libevent-2.0.22-stable
│ ├── autom4te.cache
│ ├── compat
│ │ └── sys
│ ├── include
│ │ └── event2
│ ├── m4
│ ├── sample
│ ├── test
│ └── WIN32-Code
│ └── event2
└── your_application_code_dir
In your_application_code_dir, you need to have a Makefile.am file with the following contents:
### NOTE: ### $(top_builddir) is required for libevent because there is ### an include file (event-config.h) that is generated at build-time. ### This file will be in the build directory instead of the source code ### directory if you build out-of-tree. ### AM_CPPFLAGS = -I$(top_srcdir)/libevent-2.0.22-stable/include \ -I$(top_builddir)/libevent-2.0.22-stable/include ## Omitted for clarity .. bin_PROGRAMS = your_program_name your_program_name_SOURCES = your_program_name.c your_program_name_LDADD = $(top_builddir)/libevent-2.0.22-stable/libevent_core.la ## Omitted for clarity ..The code in Makefile.am above (placed in your_application_code_dir) should be enough to make it possible to build libevent out-of-source-tree. As you see, both the include file in the build directory (out of the source code tree) and the include file in the source code tree are included. This should make it less of hassle to keep your source code tree clean all the time. Especially if you are using RCS such as subversion, git or mercurial.
Hopefully this helps those who intend to always build autotools code out-of-(source)-tree.
Subscribe to:
Posts (Atom)