Friday, April 14, 2017

Projects

A project is a great Solaris facility essential to network-wide system architecture and administration. In essence it's rather simple but in general it's less-known or frequently misunderstood or underestimated by many engineers, unfortunately. Later on defining projects, in order to take full advantage of them (and their tasks) it's necessary to enable extended accounting, but that will be a topic for a later post.

A project is a collection of network-wide (physical or virtual) hosts processes that share a same tag defined as the project-id which is shared by a collection of internetwork-ed Solaris (physical or virtual) systems. Within each participating host those corresponding processes are further gathered on tasks by means of a common task-id. In the scope of a single host, a project has always one or more subordinate tasks.

The project-ids are gathered in a project database which can be local (/etc/project) or remote (NIS, LDAP or DNS). On this post I'll cover just the local case, but you can grasp an idea of how to implement it on NIS by reading other posts of mine.

For an appropriate project setup, it's useful to distinguish host's processes that derive from a login event from those that do not, because when a login is involved there's an additional way of defining the initial project to be associated with that login, and thus its main process and all its child/derived processes. In general, when processes are launched, the selected project will be deduced from the account which is used to launch the process according to the defined associations in the standard project database of the system.

By the way, a process is tied to a single project-id (project), but be aware that a user or a group can be assigned to multiple project-ids (projects). The processes that a user launches can be associated with any project that this user is associated to (either explicitly by user-name or implicitly by groups memberships) in the standard project database of the system. Of course, the super-user can assign any process to any project.

The raw view of the out-of-the-box project database is as follows:

$ cat /etc/project
system:0::::
user.root:1::::
noproject:2::::
default:3::::
group.staff:10::::


This database can also be listed more verbosely as:

$ projects -l
system
    projid : 0
    comment: ""
    users  : (none)
    groups : (none)
    attribs:
user.root
    projid : 1
    comment: ""
    users  : (none)
    groups : (none)
    attribs:
noproject
    projid : 2
    comment: ""
    users  : (none)
    groups : (none)
    attribs:
default
    projid : 3
    comment: ""
    users  : (none)
    groups : (none)
    attribs:
group.staff
    projid : 10
    comment: ""
    users  : (none)
    groups : (none)
    attribs:


As introduced, for logins events there's an extended user attribute "database" in /etc/user_attr which may or may not list the initial (but previously defined) project to be assigned to any user's processes since upon login. If no project is specified in /etc/user_attr then the search for the assignment will proceed to /etc/project looking for a match in the following order:

  1. user.user-name
  2. group.user-group
  3. The special project called default (if present)

If no suitable project is not found, then the login event or process launch is denied.

Now let's try a few examples in order to get better acquainted with projects. Let's define the marketing project for the john account in /etc/user_attr:

# usermod -K project=marketing john

And to verify it:

# getent user_attr john
john::::project=marketing


or

# userattr project john
marketing

To remove any project for the john account in /etc/user_attr:

# usermod -K project= john

Assuming the primary group of the john account as mkt01, the (fall-back) alternatives in /etc/project for not using /etc/user_attr could be:

# projadd user.john
# projects -l user.john
user.john
        projid : ...
        comment: ""
        users  : (none)
        groups : (none)
        attribs:

or

# projadd group.mkt01
# projects -l group.mkt01
group.mkt01
        projid : ...
        comment: ""
        users  : (none)
        groups : (none)
        attribs:


or none of the above, as long as the default project is present:

# projects -l default
default
        projid : 3
        comment: ""
        users  : (none)
        groups : (none)
        attribs:


Now, another very important application of the project database is for the workloads that do not derive from a login event, but rather are typically started as services or similar background processes, such as the Apache HTTP server. Such "special kind" of workloads, when well planned and administered typically take advantage of projects for both extended accounting and resource control.

As an example, let's set up a dedicated project to an Apache HTTP server that has been just installed and is running under the webservd account. Well, that's not quite exactly the case because, in reality, what traditionally happens is that a root process is launched and forks some child processes that are assigned to a different low-privileged webservd (or other) account that (hopefully) cannot log in to the system. At first, one might think that the following output is due to no project being assigned anywhere for the webservd account, but that's only partially true:

$ id -p webservd
uid=80(webservd) gid=80(webservd) projid=3(default)


$ ps -o user,pid,ppid,project -p "`pgrep -f httpd`"
    USER   PID  PPID  PROJECT
    root   559     1  system
webservd   577   559  system
webservd   591   559  system
webservd   593   559  system
webservd   604   559  system
webservd   607   559  system
webservd   616   559  system

  
These output are expected. The id command is deducing the project for new processes that would be launched in the event of a successful webservd log in to the system, which, of course, (and hopefully) will never happen. And it's truly expected that in a standard (out-of-the-box) setup the default project gets selected in the absence of any other explicit project assignments. But for the ps command listing the explanation is different. In this last case, all httpd child processes (577, 591, 593, 604, 607 and 616) derive from the same parent process (559) which was launched as root, thus inheriting the the system project that is associated to root. In Solaris, this launch actually happens from an SMF service (svc:/network/http:apache22) method which encapsulates the standard Apache HTTP server Control Interface (/usr/apache2/2.2/bin/apachectl; APACHECTL(8)) but unfortunately omits setting a project upon firing the service.

Fixes for problems such as the one above exemplified are certainly possible, but at first it's necessary to define a new project workload (www, for instance). As good practice, not that it's actually required (due to what was explained above), the webservd account should be assigned to this new project. One way to accomplish the task takes a single projadd -U command but for demonstration purposes I'll split it into a projadd followed by a projmod -a -U:

# projadd -c "World Wide Web" www
# projects -l www
www
        projid : 100
        comment: "World Wide Web"
        users  : (none)
        groups : (none)
        attribs:


# projmod -a -U webservd www
# projects -l www
www
        projid : 100
        comment: "World Wide Web"
        users  : webservd
        groups : (none)
        attribs:


By the way, if after creating a project it became necessary to alter some of its attributes and if there are processes already running under this project, it's possible to force them to update their project assignment by means of a projmod -A command. If one is uncertain and no processes are running yet under the project (as on the Apache HTTP server example above; no one is running assigned to the www project), just a harmless warning message is displayed:

# projmod -A www
projmod: Updating ... succeeded with following warning message.
WARNING: No process found ... The project is not updated.


Now focusing on the possible fixes to the issue of having background processes running under undesired projects such as the system project in the example above, there a two solutions that I know:

1) The simpler, but less than ideal "fix" is to "manually" move the processes to the desired project. The move does not required the inconvenience of restaring the processes (in this example, the Apache HTTP server processes). The disadvantage is that it does not integrate well in an automated management as it's not integrated into SMF. The "fix" consists on issuing the following command:

# pgrep -f httpd | xargs -t -l newtask -p www -c
newtask -p www -c 577
newtask -p www -c 616
newtask -p www -c 559
newtask -p www -c 591
newtask -p www -c 593
newtask -p www -c 607
newtask -p www -c 604


Et voilĂ !

# ps -o user,pid,ppid,project -p "`pgrep -f httpd`"
    USER   PID  PPID  PROJECT
    root   559     1  www
webservd   577   559  www
webservd   591   559  www
webservd   593   559  www
webservd   604   559  www
webservd   607   559  www
webservd   616   559  www


2) The more complicated "fix" is to deal with SMF. On this post I won't go too deep in how to properly or ideally accomplish the task. I'll just indicate the central change that has to be done letting clear that by not following the proper procedures to "patch" the SMF service, the "fix" could be lost when applying future updates or when performing system upgrades. I wish I'll have time to dedicate a post on how to properly do this, but for now I suggest doing just the following:

Identify the involved SMF method:

# svccfg -s apache22 listprop start/exec
start/exec astring     "/lib/svc/method/http-apache22 start"


# ll /lib/svc/method/http-apache22
-r-xr-xr-x   1 root  bin  ... /lib/svc/method/http-apache22


Edit the method and hard-code the /usr/bin/newtask command with the desired project (www) in the indicated line (138) as highlighted below:

# chmod 775 /lib/svc/method/http-apache22

# grep -n apachectl /lib/svc/method/http-apache22
138:... /usr/bin/newtask -p www ${APACHE_BIN}/apachectl ... 2>&1


# chmod 555 /lib/svc/method/http-apache22

The next time the service restarts everything shall be fine and under the new project. If, in addition, an immediate change is required but a standard restart would risk disturbing the services, just apply the solution (1) in addition.