Friday, October 21, 2016

AI: Distro Constructor and a Custom Script

When building your own AI images with distro_const it is useful sometimes to add a custom script to modify the resulting image. This is easily achievable by adding a custom script to the xml manifest provided to distro_cons.

For example, to change the default password for user jack, add the following checkpoint to the xml file, just before pre-pkg-img-mode checkpoint. 

<!--
  Set password to user jack, should match root password
  (if hash contains slashed they need to be backslashed)
-->
      <checkpoint name="lock-jack-account"
         desc="Lock the jack account from login"
         mod_path="solaris_install/distro_const/checkpoints/custom_script"
         checkpoint_class="CustomScript">
         <args>/usr/bin/gsed -i -e 's/jack:.[^:]*:/jack:XXXXXX:/g' 
                             {PKG_IMAGE_PATH}/etc/shadow
         </args>
      </checkpoint>

Tuesday, October 11, 2016

Requiring both GSSAPI and OTP

Darren Moffat blogged about how to force both GSSAPI (or pubkey) and OTP on Solaris in OpenSSH. This works, although is not entirely obvious how to set it up at first.

Friday, July 01, 2016

SPARC S7

Oracle released new SPARC S7 CPU and SPARC S7-2 and S7-2L servers. This is really interesting SPARC CPU if you need low-end servers, the first one in many, many years which can compete with x86 both in performance and price. It has some unique features as well.

See launch video.

Various articles on S7:

TheNextPlatform
The Register
PCWorld
ComputerWorld

Also see some benchmarks already published:

SPECjbb2015
SPECjEnterprise2010
Database: S7 vs x86
Yahoo Cloud Serving Benchmark


Friday, May 20, 2016

Adjusting SO_RCVBUF of a running process

Recently I was looking into how to increase SO_RCVBUF size for a given socket in a running process, without having to restart it. This could be useful, if an application can't be restarted anytime soon, yet there are drops observed due to too low receive buffer set, or perhaps a given application doesn't even allow for the receive buffer to be specified and has a hard-coded value. In my case, an application does allow for the buffer to be specified, but it only sets it on startup and I couldn't restart it.

Solaris (nor Linux AFAIK) does not provide a tool to easily adjust the buffer for a socket in a running process, so I looked if I could do it via libproc. The answer is yes, and it is pretty straightforward.

I quickly wrote a small C program which changes the SO_RCVBUF size for a given pid and file descriptor number. Let's see an example on how to use it.

There is a process with pid 893 listening on port UDP/32623 with the SO_RCVBUG currently set to 128104:

# pfiles 893
893:    /usr/local/bin/test-daemon
  Current rlimit: 256 file descriptors
...
   4: S_IFSOCK mode:0666 dev:574,0 ino:43685 uid:0 gid:0 size:0
      O_RDWR|O_NONBLOCK FD_CLOEXEC
      SOCK_STREAM
      SO_REUSEADDR,SO_SNDBUF(49152),SO_RCVBUF(128104)
      sockname: AF_INET 0.0.0.0  port: 32623
      congestion control: newreno
...

Let's change the SO_RCVBUG to a higher value:

# ./pr_setsockopt 893 4 500000
Current SO_RCVBUG is 128104
New SO_RCVBUG is 500088

# pfiles 893
...
   4: S_IFSOCK mode:0666 dev:574,0 ino:43685 uid:0 gid:0 size:0
      O_RDWR|O_NONBLOCK FD_CLOEXEC
      SOCK_STREAM
      SO_REUSEADDR,SO_SNDBUF(49152),SO_RCVBUF(500088)
      sockname: AF_INET 0.0.0.0  port: 32623
      congestion control: newreno
...

The code is very similar to the one I wrote last time. However, as there is no pr_setsockopt() wrapper function, I wrote one based on how the other pr_* functions are implemented, specifically pr_getsockopt(). The trick is that there is Psyscall() function available, which allows you to call any syscall from the target process, so all that is required is to use it to call SYS_setsockopt.

As the source code for Solaris is no longer publicly available, I used Illumos source code. The program was tested only on Solaris 11 x86, although it probably works fine on Solaris 10 and Illumos, and should work on SPARC as well.

It is a quick "hack", with no safeguards, no proper argument parsing, etc.
Use it at your own risk.

// gcc -m64 -lproc -o pr_setsockopt pr_setsockopt.c

#include <sys/types.h>
#include <sys/mman.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <libproc.h>


pr_setsockopt(struct ps_prochandle *Pr, int sock, int level, int optname, void *optval, int optlen) {
  sysret_t rval;  /* return value from getsockopt() */
  argdes_t argd[5]; /* arg descriptors for getsockopt() */
  argdes_t *adp;
  int error;

  if (Pr == NULL)  /* no subject process */
    return (_so_setsockopt(sock, level, optname, optval, optlen));

  adp = &argd[0];  /* sock argument */
  adp->arg_value = sock;
  adp->arg_object = NULL;
  adp->arg_type = AT_BYVAL;
  adp->arg_inout = AI_INPUT;
  adp->arg_size = 0;

  adp++;   /* level argument */
  adp->arg_value = level;
  adp->arg_object = NULL;
  adp->arg_type = AT_BYVAL;
  adp->arg_inout = AI_INPUT;
  adp->arg_size = 0;

  adp++;   /* optname argument */
  adp->arg_value = optname;
  adp->arg_object = NULL;
  adp->arg_type = AT_BYVAL;
  adp->arg_inout = AI_INPUT;
  adp->arg_size = 0;

  adp++;   /* optval argument */
  adp->arg_value = 0;
  adp->arg_object = optval;
  adp->arg_type = AT_BYREF;
  adp->arg_inout = AI_INPUT;
  adp->arg_size = optlen == NULL ? 0 : optlen;

  adp++;   /* optlen argument */
  adp->arg_value = optlen;
  adp->arg_object = NULL;
  adp->arg_type = AT_BYVAL;
  adp->arg_inout = AI_INPUT;
  adp->arg_size = 0;

  error = Psyscall(Pr, &rval, SYS_setsockopt, 5, &argd[0]);

  if (error) {
    errno = (error > 0)? error : ENOSYS;
    return (-1);
  }
  return (0);
}


int main(int argc, char **argv) {
  pid_t pid;
  int fd;
  int perr;
  static struct ps_prochandle *Pr;

  pid = atop(argv[1]);
  fd = atoi(argv[2]);

  if((Pr = Pgrab(pid, PGRAB_NOSTOP, &perr)) == NULL) {
    printf("Pgrab() failed: %s\n", Pgrab_error(perr));
    exit(1);
  }

  int rcvbuf = 0;
  int rcvbuf_size = sizeof(rcvbuf);
  if(pr_getsockopt(Pr, fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, &rcvbuf_size)) {
    perror("pr_getsockopt() failed");
    Prelease(Pr, 0);
    exit(1);
  }

  printf("Current SO_RCVBUF is %d\n", rcvbuf);

  rcvbuf = atoi(argv[3]);
  if(pr_setsockopt(Pr, fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, rcvbuf_size)) {
    perror("pr_setsockopt() failed");
    Prelease(Pr, 0);
    exit(1);
  }

  if(pr_getsockopt(Pr, fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, &rcvbuf_size)) {
    perror("pr_getsockopt() failed");
    Prelease(Pr, 0);
    exit(1);
  }

  printf("New SO_RCVBUF is %d\n", rcvbuf);

  Prelease(Pr, 0);
  Pr = NULL;

  exit(0);  
}

Wednesday, March 02, 2016

Full command line returned by ps

ps command can now show full command line on Solaris 11 as well. Thank you Casper.
For more details see here.