Friday, April 28, 2017

Resource control - rc-03

This program (rc-03) uses the Solaris interface to resource control.
It lists all the controls that are active to its running process.
Following the listing there's a sample output.

#include <rctl.h>
#include <alloca.h>

#include <cstdlib>
#include <cerrno>
#include <string>
#include <vector>
#include <map>
#include <algorithm>
#include <iterator>
#include <sstream>
#include <iostream>
#include <iomanip>

//
// Convenience "special" vector.

//
template< typename T >
struct vector : public std::vector< T >
{
};
 
//
// Convenience operator for just the "special" vector. 
// For simplicity, not dealing with formatting or state issues.
//
template< typename T >
std::istream & operator >> ( std::istream & is, vector<T> & v )
{
  typename vector<T>::value_type element;

  if ( is >> element )
    v.push_back( element );

  return is;
}

//
// The main data structure for organization.
// Key: resource containment.
// Data: list of controls.
//
std::map< const std::string, vector< std::string > > resource;

//

// The resource walking routine.
// 
int active( const char * name, void * )
{
  std::istringstream iss( name );
  std::string containment;

  if ( std::getline( iss, containment, '.' ) )
    iss >> resource[ containment ];

  return 0;
}

const size_t size = ::rctlblk_size( );
typedef ::rctlblk_t * p_blk;

// May suffer from stream formatting issues...
void print_limit( const p_blk rl )
{
  //
  // Type
  //

  std::cout
    << "\t\t"
    << std::setw( 11 ) << std::left;

  switch ( ::rctlblk_get_privilege( rl ) )
  {
      case RCPRIV_BASIC:
          std::cout << "basic";
          break;

      case RCPRIV_PRIVILEGED:
          std::cout << "privileged";
          break;

      case RCPRIV_SYSTEM:
          std::cout << "system";
          break;
  }
 
  //
  // Value
  //

  std::cout
     << ":"
     << std::setw( 21 ) << std::right
     << ::rctlblk_get_value( rl );

  std::cout
     << std::endl; 
 
  std::cout 
     << std::setw( 0 ) << std::left;
}

// May suffer from stream formatting issues... 
void print_control( std::string containment, 
                    vector< std::string > & control_list )

  p_blk rl = static_cast < p_blk >
             (
                 // Never leaks memory
                 ::alloca( size )
             );

  containment += ".";
 
  std::sort( control_list.begin( ), control_list.end( ) );

  for ( auto & control : control_list )
  {
    std::cout
       << "\t";
  
    std::cout
     << std::setw( 32 ) << std::left
     << control;


    const std::string name = containment + control;
   
    p_blk p = 0; // Tracking pointer
    int rv = ::getrctl( name.c_str(), p, rl, RCTL_FIRST );

    const int gflags = ::rctlblk_get_global_flags( rl );
    std::cout
     << std::setw( 9 ) << std::right
     <<
     (
     gflags & RCTL_GLOBAL_BYTES   ? "(bytes)" :
     gflags & RCTL_GLOBAL_SECONDS ? "(seconds)" :
     gflags & RCTL_GLOBAL_COUNT   ? "(count)" :
     "(?)"
     )
     << std::endl;

    std::cout
     << std::setw( 0 ) << std::left;


    while ( rv == 0 )
    {
        print_limit( rl );

        p = rl; // Smart(?) move...
        rv = ::getrctl( name.c_str(), p, rl, RCTL_NEXT );
    }

    if ( errno != ENOENT )
      std::cout
         << "\t\tError getting limits!"
         << std::endl;
   
    std::cout
         << std::endl;
  }
}

// May suffer from stream formatting issues... 
void print( )
{
  std::cout
     << std::endl
     << "Printing active resource controls..."
     << std::endl
     << std::endl;

  // I want to list containments as ordered below
  auto known = { "process", "task", "project", "zone" };

  for ( auto & containment : known )
  {
    std::cout
       << containment
       << std::endl
       << std::endl;
   
    print_control( containment, resource[ containment ] );
  }

  //
  // In case new (currently unknown) containments appear
  // in the future, just list them at the bottom.
  //
  for ( auto & r : resource )
  {
    auto c = r.first; // A containment
    if ( std::none_of( known.begin( ), known.end( ),
            [ &c ]( const char * s ) { return s == c; } ) )
    {
      std::cout
         << c // An unknown containment
         << std::endl
         << std::endl;
     
      print_control( r.first, r.second );
    }
  }
}

// May suffer from stream formatting issues... 
int main( )
{
  std::cout
     << std::endl
     << "Gathering active resource controls..."
     << std::endl
     << std::endl;

  std::cout
     << "\tRC walk starting..."
     << std::endl;

  if ( ::rctl_walk( active, 0 ) == 0 )
    std::cout
     << "\tRC walk completed successfuly!"
     << std::endl;

  print( );

  return EXIT_SUCCESS;
}


A possible output is:

$ ./rc-03

Gathering active resource controls...

    RC walk starting...
    RC walk completed successfuly!

Printing active resource controls...


process

    max-address-space             (bytes)
        privileged : 18446744073709551615
        system     : 18446744073709551615

    max-core-size                 (bytes)
        privileged :  9223372036854775807
        system     :  9223372036854775807

    max-cpu-time                (seconds)
        privileged : 18446744073709551615
        system     : 18446744073709551615

    max-data-size                 (bytes)
        privileged : 18446744073709551615
        system     : 18446744073709551615

    max-deferred-posts            (count)
        basic      :                   32
        privileged :                  100
        system     :                 8192

    max-file-descriptor           (count)
        basic      :                 1024
        privileged :                65536
        system     :           2147483647

    max-file-size                 (bytes)
        privileged :  9223372036854775807
        system     :  9223372036854775807

    max-itimers                   (count)
        privileged :                 1000
        system     :                65536

    max-msg-messages              (count)
        privileged :                 8192
        system     :           4294967295

    max-msg-qbytes                (bytes)
        privileged :                65536
        system     : 18446744073709551615

    max-port-events               (count)
        privileged :                65536
        system     :           2147483647

    max-sem-nsems                 (count)
        privileged :                  512
        system     :                32767

    max-sem-ops                   (count)
        privileged :                  512
        system     :           2147483647

    max-sigqueue-size             (count)
        basic      :                  128
        privileged :                  512
        system     :                 8192

    max-stack-size                (bytes)
        basic      :              8388608
        privileged :        1098437885952
        system     :        1098437885952

task

    max-cpu-time                (seconds)
        system     : 18446744073709551615

    max-lwps                      (count)
        system     :           2147483647

    max-processes                 (count)
        system     :           2147483647

project

    cpu-cap                       (count)
        system     :           4294967295

    cpu-shares                    (count)
        privileged :                    1
        system     :                65535

    max-contracts                 (count)
        privileged :                10000
        system     :           2147483647

    max-crypto-memory             (bytes)
        privileged :           2143089664
        system     : 18446744073709551615

    max-locked-memory             (bytes)
        system     : 18446744073709551615

    max-lwps                      (count)
        system     :           2147483647

    max-mrp-ids                   (count)
        privileged :                  128
        system     :             16777216

    max-msg-ids                   (count)
        privileged :                  128
        system     :             16777216

    max-port-ids                  (count)
        privileged :                 8192
        system     :                65536

    max-processes                 (count)
        system     :           2147483647

    max-sem-ids                   (count)
        privileged :                  128
        system     :             16777216

    max-shm-ids                   (count)
        privileged :                  128
        system     :             16777216

    max-shm-memory                (bytes)
        privileged :           2143089664
        system     : 18446744073709551615

    max-tasks                     (count)
        system     :           2147483647

zone

    cpu-cap                       (count)
        system     :           4294967295

    cpu-shares                    (count)
        privileged :                    1
        system     :                65535

    max-locked-memory             (bytes)
        system     : 18446744073709551615

    max-lofi                      (count)
        system     : 18446744073709551615

    max-lwps                      (count)
        system     :           2147483647

    max-mrp-ids                   (count)
        system     :             16777216

    max-msg-ids                   (count)
        system     :             16777216

    max-processes                 (count)
        system     :           2147483647

    max-sem-ids                   (count)
        system     :             16777216

    max-shm-ids                   (count)
        system     :             16777216

    max-shm-memory                (bytes)
        system     : 18446744073709551615

    max-swap                      (bytes)
        system     : 18446744073709551615



Thursday, April 27, 2017

Resource control - rc-02

This program (rc-02) uses the Solaris interface to resource control.
The main() function was listed first just to let the logic more clear.
Note how its approach is more complex than the traditional one:

#include <rctl.h> 
#include <strings.h>
#include <alloca.h>
#include <unistd.h>

  
#include <cerrno>
#include <cstdlib>
#include <iostream>

  
const ::pid_t ppid = ::getppid( );
const size_t size = ::rctlblk_size(); 
static const char name[] = "process.max-file-descriptor";

typedef ::rctlblk_t * p_blk;

// May suffer from stream formatting issues... 
int main( )
{
    std::cout
        << std::endl
        << "Resource limit: " << name
        << std::endl;

    
    p_blk p = 0; // Tracking pointer
    p_blk rl = static_cast < p_blk

               (
                   // Never leaks memory
                   ::alloca( size ) 
               );

    int rv = ::getrctl( name, p, rl, RCTL_FIRST );
    while ( rv == 0 )
    {

        print( rl );

        p = rl; // Smart(?) move...
        rv = ::getrctl( name, p, rl, RCTL_NEXT );
    }


    if ( errno != ENOENT )
    {

        ...
        return EXIT_FAILURE;
    }


    return EXIT_SUCCESS;
}


And now the auxiliary printing function:

// May suffer from stream formatting issues... 
void print( const p_blk rl )
{

    std::cout << std::endl;

    //

    // Type
    //

    std::cout << "\tType: ";
    switch (
::rctlblk_get_privilege( rl ) )
    {
        case RCPRIV_BASIC:
            std::cout << "Basic";
            break;

        case RCPRIV_PRIVILEGED:
            std::cout << "Privileged";
            break;

        case RCPRIV_SYSTEM:
            std::cout << "System";
            break;
    }

  
    //
    // PID & PPID
    //

    const ::id_t pid = ::rctlblk_get_recipient_pid( rl );

    std::cout << " (PID " << pid;

    if ( pid > -1 )
        std::cout << "; PPID " << ppid;

    std::cout << ")" << std::endl;


 
   //
    // Flags
    //

    const int gflags = ::rctlblk_get_global_flags( rl );

    std::cout << "\tGlobal action: "
        <<
        (
        gflags == RCTL_GLOBAL_NOACTION ? "no action" :
        gflags & RCTL_GLOBAL_DENY_ALWAYS ? "deny" :
        gflags & RCTL_GLOBAL_DENY_NEVER ? "allow" :
        "other"
        )
        << "; ";

    std::cout << "Syslog: "
        <<
        (
        gflags & RCTL_GLOBAL_SYSLOG ? "yes" : "no"
        )
        << std::endl;

    std::cout << "\tLowerable: "
        <<
        (
        gflags & RCTL_GLOBAL_LOWERABLE ? "yes" : "no"
        )
        << "; ";

    std::cout << "Infinite: "
        <<
        (
        gflags & RCTL_GLOBAL_INFINITE ? "yes" : "no"
        )
        <<
std::endl;

    const int lflags = ::rctlblk_get_local_flags( rl );

    std::cout << "
\tLocally maximal: "
        <<
        (
        lflags & RCTL_LOCAL_MAXIMAL ? "yes" : "no"
        )
        <<
"; ";

    std::cout << "Local project: "
        <<
        (
        lflags & RCTL_LOCAL_PROJDB ? "yes" : "no"
        )
        << std::endl;
 

 
    std::cout << "\tLimit type: "
        <<
        (
        gflags & RCTL_GLOBAL_BYTES ? "bytes" :
        gflags & RCTL_GLOBAL_SECONDS ? "seconds" :
        gflags & RCTL_GLOBAL_COUNT ? "count" :
        "?"
        )
        << std::endl;

   
    //
    // Values
    //

    std::cout << "\tCurrent:  "
        << ::rctlblk_get_value( rl )
        << std::endl;

    std::cout << "\tEnforced: "
        << ::rctlblk_get_enforced_value( rl )
        << std::endl;

}
  
This program works in Solaris 11.3.
I used the GCC 4.8.2 and NetBeans 8.1 with no issues at all.

Resource control - rc-01

This program (rc-01) uses the UNIX portable interface to resource control.
This is the traditional (legacy) standard as well.

Note how simple it is!
But the recommended Solaris approach is far more complex...

#include <sys/resource.h>

#include
<cstdlib>
#include
<iostream
  
// May suffer from stream formatting issues... 
int main( )
{
    ::rlimit rl;

    if ( ::getrlimit( RLIMIT_NOFILE, &rl ) != 0 )
    {
        ...
        return EXIT_FAILURE;
    }

    std::cout << "Process' inherited FD limits:" << std::endl;
    std::cout << "Current: " << rl.rlim_cur << "." << std::endl;
    std::cout << "Maximum: " << rl.rlim_max << "." << std::endl;

    return EXIT_SUCCESS;
}


This program works in Solaris 11.3.
I used the GCC 4.8.2 and NetBeans 8.1 with no issues at all.
 

Monday, April 24, 2017

Resource control - Intro

Resource control historically appeared for limiting the system's resources that processes and their children could consume, but nowadays in Solaris this concept has been elaborated to other collections of processes: tasks, projects and zones as well.

The best practice is to carefully assess (as using extended accounting) the resource consumption of the workloads on the system before applying any fine-grained resource control to prevent over-consumptions. And, of course, above all, the system must meet or exceed the combined resource requirements of all the workloads it's supposed to host.

This topic is vast because there are many resources (resource-controls(5)) ranging from the most "elementary" to the most complex ones, there 3 control levels (basic, privileged and system), there are 4 containment levels (process, task, project and zone), 2 types of actions and flags (local and global) as well more than one available interface managing part (ulimit(1) and getrlimit(2)) or all of this stuff (rctladm(1M), prctl(1), setrctl(2), the projects database and zone configuration).

All the manpages provide extensive information I won't discuss, at least for now. In addition there are some other lenghty references such as the chapter 5 of Resource Management and Oracle® Solaris Zones Developer's Guide which is a kind of revamp of the original chapter 5 of the (partially) archived Solaris Containers: Resource Management and Solaris Zones Developer's Guide.
 

Saturday, April 22, 2017

Sessions & Process Groups

In the very start of the endeavor to the daemons' underground world it's inevitable to be acquainted with a few, perhaps new, essential concepts about processes in terms of how they are collected within the system.

Without further digressions, processes are collected into process groups. But there's an elaboration on top of this leading to another concept called sessions, which is associated to the historical concept of job control from a time lacking the pletora of GUI terminals if not, perhaps, just the rare and crude xterm which some ancient seems to inconditionally love.

Although not really a big mistery, the descriptions about these concepts (process groups, sessions and their related) seems unecessarily convoluted in Intro(2) and termio(7I), which can partially obfuscate understanding its central apparatus. My intention is, thus, to address these obstacules by attempting to streamline some ideas and clarify some of these concepts definitions and descriptions:

PROCESS GROUP
A collection of processes that begins when created by a given process which is thereof defined as its process group leader. The process group ends when the last process in the process group ends of leaves the process group.

During its lifetime it's uniquely identified by a positive integer called the process group ID (PGID), which exactly matches the process ID (PID) of its process group leader and furnishes a way of signaling all its member processes as a whole.

Any process that is not a process group leader may create a new process group or a new session (becoming itself the process group leader and, eventually, the session leader of the newly created entity) or yet may join another process group in the same session.

Each process in the system is a member of a single process group and every newly created process joins the process group and session of its parent (process).
SESSION
A collection of process groups that begins when created by a given process which is thereof defined as its session leader. The session ends when the last process in the session ends of leaves the session

During its lifetime it's uniquely identified by a positive integer called the session ID (SID), which exactly matches the process ID (PID) of its original session leader. The original session leader is also the process group leader for the session's originally first process group, hence its process ID (PID) matches its session ID (SID) and its process group ID (PGID) and, as such, the session ID (SID) also matches its originally first process group ID (PGID).

A session is capable of being biunivocally (one-to-one) associated to a terminal (the controlling terminal, /dev/tty according to tty(7D)) by the session leader (the controlling process) which establishes the connection. As sessions are inherited by new processes created within it, so are the controlling terminals, although this association can be broken if these new processes establish a new session (setsid(2)). The controlling terminal can send/handle certain (typically quit and interrupt) signals to the process groups in the (controlling process') session that, in general, will cause them to stop, unless they have made arrangements to mask this out or are orphaned process groups.

Orphaned process groups are those that share the controlling terminal but whose processes' parents do not belong to any other process group in the same session.

Among the process groups in the session, the controlling terminal will distinguish one of them as the foreground process group, thereby exclusively granting it certain terminal access prerrogatives, and treat all the other process groups in that (controlling process') session as background process groups. By default, the controlling process' (session leader's) process group is assigned as the foreground process group.
 
As for a real world example of a few of these concepts in action applied to a daemon creation you may be interested in browsing the output from truss applied to a sample daemon creation code around the fork() and setsid() system calls and associated PIDs and PPIDs numbers.
 

Friday, April 21, 2017

Daemons listing

Without entering into any further detail about what a daemon is, I'll just show how to list them under a privileged (root) Solaris terminal by means of the standard shell command ps(1):

# ps -o user,uid,pid,ppid,pgid,sid,tty,args -t '?' \
    |(IFS=''; read H; echo "$H"; sort -k5 -k3 \
    |grep filtering_options account )
 
    USER   UID   PID  PPID  PGID   SID TT COMMAND
    root     0     1     0     0     0 ?  /usr/sbin/init
    root     0     3     0     0     0 ?  fsflush
    root     0     7     0     0     0 ?  intrd
    root     0     6     0     0     0 ?  kmem_task
    root     0     2     0     0     0 ?  pageout
    root     0     9     0     0     0 ?  postwaittq
    root     0     0     0     0     0 ?  sched
    root     0     8     0     0     0 ?  vmtasks
    root     0   770     0     0     0 ?  zpool-depot
    root     0   788     0     0     0 ?  zpool-export
    root     0     5     0     0     0 ?  zpool-rpool
    root     0    96     1    13    13 ?  /lib/svc/bin/...
    root     0    13     1    13    13 ?  /lib/svc/bin/svc.startd
    root     0   751     1    13    13 ?  /lib/svc/method/iscsid
    root     0  1862  1353    13    13 ?  /usr/lib/...
    root     0  1353  1341    13    13 ?  /usr/lib/...
    root     0   106     1    13    13 ?  /usr/lib/pfexecd
    root     0   155     1    13    13 ?  /usr/lib/rad/rad -sp
    root     0   164     1    13    13 ?  /usr/lib/rad/rad -sp
    root     0  1341     1    13    13 ?  /usr/sbin/gdm-binary
    root     0    15     1    15    15 ?  /lib/svc/bin/...
  netcfg    17    42     1    42    42 ?  /lib/inet/netcfgd
  netadm    16    45     1    45    45 ?  /usr/sbin/ibmgmtd
   dladm    15    53     1    53    53 ?  /usr/sbin/dlmgmtd
  netadm    16    66     1    66    66 ?  /lib/inet/ipmgmtd
  daemon     1    74     1    74    74 ?  /lib/crypto/kcfd
    root     0    84     1    83    83 ?  /lib/inet/in.mpathd
  daemon     1   149     1   149   149 ?  /usr/lib/utmpd
    root     0   178     1   178   178 ?  /usr/sbin/vbiosd
    root     0   185     1   185   185 ?  /usr/lib/zones/...
    root     0   200     1   200   200 ?  /usr/lib/sysevent/...
    root     0   332     1   332   332 ?  /usr/lib/dbus-daemon ...
    root     0   430     1   430   430 ?  /usr/lib/devfsadm/...
  netadm    16   648     1   648   648 ?  /lib/inet/nwamd
    root     0   688     1   688   688 ?  /usr/lib/picl/picld
    root     0   757     1   757   757 ?  /usr/lib/inet/ntpd ...
    root     0   786     1   786   786 ?  /usr/sbin/nscd
  daemon     1   800     1   800   800 ?  /usr/sbin/rpcbind
    root     0   813     1   813   813 ?  /usr/lib/inet/in.ndpd
    root     0   842     1   840   840 ?  /opt/VirtualBox/...
    root     0   843     1   843   843 ?  /usr/lib/inet/...
    root     0   856     1   856   856 ?  /usr/lib/hotplugd
    root     0   860     1   860   860 ?  /usr/lib/autofs/...
    root     0   862   860   860   860 ?  /usr/lib/autofs/...
    root     0   866     1   865   865 ?  /usr/sbin/...
    root     0   870     1   870   870 ?  /usr/sbin/cron
    root     0   876     1   876   876 ?  /usr/lib/hal/...
    root     0   899   878   876   876 ?  /usr/lib/hal/...
    root     0   895   878   876   876 ?  /usr/lib/hal/...
    root     0   977   878   876   876 ?  /usr/lib/hal/...
    root     0  1160   878   876   876 ?  /usr/lib/hal/...
    root     0  1184   878   876   876 ?  /usr/lib/hal/...
    root     0  1206   878   876   876 ?  /usr/lib/hal/...
    root     0  1219   878   876   876 ?  /usr/lib/hal/...
    root     0  1236   878   876   876 ?  /usr/lib/hal/...
    root     0   878   876   876   876 ?  hald-runner
    root     0   942     1   941   941 ?  /usr/lib/ssh/sshd
    root     0   983     1   983   983 ?  /usr/sbin/syslogd
    root     0  1019     1   987   987 ?  /usr/sbin/auditd
   smmsp    25  1005     1  1005  1005 ?  /usr/lib/inet/...
    root     0  1006     1  1006  1006 ?  /usr/lib/inet/...
    root     0  1024     1  1024  1024 ?  /usr/lib/fm/fmd/fmd
    root     0  1126     1  1125  1125 ?  /usr/bin/python2.7 ...
    root     0  1140     1  1140  1140 ?  /usr/sbin/cupsd -C ...
noaccess 60002  1228     1  1228  1228 ?  /usr/lib/fm/notify/...
    root     0  1232     1  1232  1232 ?  /usr/lib/devchassis/...
noaccess 60002  1234     1  1234  1234 ?  /usr/lib/fm/notify/...
    root     0  1349     1  1348  1348 ?  /usr/lib/rmvolmgr -s
     ocm    62  1639     1  1639  1639 ?  /usr/lib/ocm/ccr/...
     gdm    50  1652     1  1649  1649 ?  /usr/bin/dbus-launch
...


A basic description of each chosen column is on the aforementioned manpage, but it was driven by an interest towards daemon programming. One will note that the main filtering criteria is the absence ('?') of a controlling terminal (TT) and that the output was sorted first by process groups (PGID) and then by process ids (PID). Perhaps one will also note (with the help of Intro(2) manpage) that in this listing each process is a member of the first process group in/of each session because its PGID and SID are equal.