Friday February 27, 2009
Adding debugging functionality into your app!
In this blog I will just toss out some small ideas you may use if you would like to make your application a little bit more debug friendly while you are developing on it. The first little example is a function that you may call from your application to set a memory watchpoint on a memory area, so that you can trap "dangeling" pointers writing to your data structures. When do you want to use this? Well let's say that you when you develop your application it seems that someone change your data structure, and you cannot figure out how this is happening. One solution would be to run the program in the debugger and set a watchpoint on the memory area, but let's say that it happens only 1 out of a 1000 times. A better solution would probably be to set the watchpoint from your application code. The following little function does exactly that:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <procfs.h>
#include <sys/fault.h>
void memory_watchpoint(const void *ptr, size_t size, int mode)
{
int fd = open("/proc/self/ctl", O_WRONLY);
if (fd != -1) {
typedef struct {
long cmd;
prwatch_t prwatch;
} ctl_t;
ctl_t ctls = { .cmd = PCWATCH,
.prwatch.pr_vaddr = (uintptr_t)ptr,
.prwatch.pr_size = size };
if (mode & 1) {
ctls.prwatch.pr_wflags = WA_WRITE;
}
if (mode & 2) {
ctls.prwatch.pr_wflags |= WA_READ;
}
(void)write(fd, &ctls, sizeof(ctls));
(void)close(fd);
}
}
I would now turn on the watchpoint for the memory area when I'm done using it, and disable the watchpoint right before I intent to use it etc. Solaris would then trigger when someone tries to access the memory without disabling the watchpoint first.
Another thing you could do is to stop your binary when you detect a problem in your application instead of terminating the problem. Personally I hate to read log-files to try to figure out why the process exited (in 99,9% of the times you don't have all the information you want), and getting core-file is a big step in the right direction. My favorite is however when I can attach a debugger to the process instead:
#include <unistd.h>
#include <fcntl.h>
#include <procfs.h>
#include <unistd.h>
void stop_process(void)
{
int fd = open("/proc/self/ctl", O_WRONLY);
if (fd != -1) {
long cmds[2] = {PCSTOP, 0};
(void)write(fd, &cmds[0], sizeof(cmds));
(void)close(fd);
}
}
If you can't stop your server (let's say it's a database server), you could always call fork() first to let the parent continue and debug the child process ;-)
This is just two small examples you may use to make your application more debug friendly. You can take a look at proc(4) for more information on what you may use the /proc filesystem for. You will see that the /proc-filesystem on Solaris differs from the one on Linux in the way that the Linux /proc-filesystem is more optimized for the human eye (you may just cat the file, but Solaris provides a lot of tools to operate on the /proc-filesystem. See pmap, pstack, pfiles, pldd to mention a few (some of the tools works on core-files as well!! (and you may specify the thread you want to look at as well). I encourage you to look at the man pages.
Posted at 10:13PM Feb 27, 2009 by trond in OpenSolaris | Comments[0]
Wednesday February 25, 2009
Creating a relocatable binary
When you create a large project you would most likely want arrange your
code into logical parts and create various libraries containing logical
units of code. In the "good old days" this would normally be an archive
of object files created by using ar.
ex:
trond@opensolaris> ar -r myarchive.a foo1.o foo2.o foo3.o ar: creating myarchive.a
You would pass the archive to the archive to the linker when you linked your program like:
trond@opensolaris> cc -o myprog main.o myarchive.a
The linker would now search for a definition for all of the undefined
symbols in main.o in myarchive.a. (please note
that if you named your archive libmyarchive you could also
pass it to the linker as -lmyarchive). This is what we call
static linking, but static linking have some obvious drawbacks:
A better solution is to create relocatable objects (aka shared libraries, dll etc), and link the application with those instead (and if you look at a default installation of Solaris you will not find any of the system libraries as static archives). Unfortunately relocatable objects have it's own problems, and that is what I'll address in the rest of this blog post.
So let's go ahead and create a small example to look at the problems and
how to solve them. I have created two small source files: lib.c
contains the function I want in my library, and main.c contains
my application:
trond@opensolaris> cat lib.c
#include <stdio.h>
void my_print(int val) {
fprintf(stdout, "The value is %d\n", val);
}
We compile this into a relocatable object by using the -Kpic
option to cc, and create a shared object by using the
-G.
trond@opensolaris> cc -c -Kpic lib.c trond@opensolaris> cc -o libfoo.so -G lib.o
The next thing we need to do is to compile our main program and link it with the library.
trond@opensolaris> cat main.c
extern void my_print(int);
int main(int argc, char **argv) {
my_print(argc);
return 0;
}
trond@opensolaris> cc -c main.c
trond@opensolaris> cc -o myprog main.o -lfoo
ld: fatal: library -lfoo: not found
ld: fatal: file processing errors. No output written to myprog
This doesn't work! Why? By default the linker will only look for shared
libraries in /lib[/64] and /usr/lib[/64] (see
Directories Searched by the Runtime Linker
for more information. To instruct the linker to search the current directory
we need to pass -Lpath to the link step (or we could
use LD_LIBRARY_PATH, but you don't want to use LD_LIBRARY_PATH...
Why? ask Google: LD_LIBRARY_PATH evil)
trond@opensolaris> cc -o myprog main.o -L. -lfoo
It compiled just fine, so let's try start it:
trond@opensolaris> ./myprog ld.so.1: myprog: fatal: libfoo.so: open failed: No such file or directory
I see multiple solutions to this problem:
libfoo.so to /usr/lib, but it really
doesn't belong in there...LD_LIBRARY_PATH and
invokes my binary (but I would prefer to avoid using LD_LIBRARY_PATH if
it is possiblecrle (this is
the Solaris version of ldconf) to include the directory
containing my library into the list the runtime linker will search for
libraries in. I don't think this is a good idea as well, because I don't
think all of the other programs want my library (and this feels a bit
like a really global LD_LIBRARY_PATH hack...)trond@opensolaris> cc -o myprog main.o -L. -R. -lfoo trond@opensolaris> ./myprog The value is 1
So I guess I'm done now.. Let's install my binary in my $HOME/bin directory and try it :-)
trond@opensolaris> cp myprog libfoo.so ~/bin trond@opensolaris> myprog ld.so.1: myprog: fatal: libfoo.so: open failed: No such file or directory
WHAT! It doesn't work!! So what happens when we try to run the binary? let's look:
trond@opensolaris> truss /home/trond/bin/myprog
execve("/home/trond/bin/myprog", 0x08047704, 0x0804770C) argc = 1
mmap(0x00000000, 4096, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANON, -1, 0) = 0xFEFB0000
resolvepath("/usr/lib/ld.so.1", "/lib/ld.so.1", 1023) = 12
resolvepath("/home/trond/bin/myprog", "/home/trond/bin/myprog", 1023) = 30
stat64("/home/trond/bin/myprog", 0x08047328) = 0
open("/var/ld/ld.config", O_RDONLY) Err#2 ENOENT
sysconfig(_CONFIG_PAGESIZE) = 4096
stat64("./libfoo.so", 0x08046B08) Err#2 ENOENT
stat64("/lib/libfoo.so", 0x08046B08) Err#2 ENOENT
stat64("/usr/lib/libfoo.so", 0x08046B08) Err#2 ENOENT
ld.so.1: myprog: fatal: libfoo.so: open failed: No such file or directory
write(2, " l d . s o . 1 : m y p".., 74) = 74
lwp_self() = 1
It tries to look for libfoo.so in the current directory!!!
let's take a peak in the binary (I have bolded out
the interesting pieces):
trond@opensolaris> dump -Lv myprog myprog: **** DYNAMIC SECTION INFORMATION **** .dynamic: [INDEX] Tag Value [1] NEEDED libfoo.so [2] NEEDED libc.so.1 [3] INIT 0x8050a20 [4] FINI 0x8050a3c [5] RUNPATH . [6] RPATH . [7] HASH 0x8050118 [8] STRTAB 0x805042c [9] STRSZ 0x37f [10] SYMTAB 0x805028c [11] SYMENT 0x10 [12] SUNW_SYMTAB 0x80501fc [13] SUNW_SYMSZ 0x230 [14] SUNW_SORTENT 0x4 [15] SUNW_SYMSORT 0x8050810 [16] SUNW_SYMSORTSZ 0x38 [17] CHECKSUM 0x83d2 [18] VERNEED 0x80507ac [19] VERNEEDNUM 0x1 [20] PLTSZ 0x30 [21] PLTREL 0x11 [22] JMPREL 0x8050850 [23] REL 0x8050848 [24] RELSZ 0x38 [25] RELENT 0x8 [26] DEBUG 0 [27] FEATURE_1 PARINIT [28] SUNW_CAP 0x8050108 [29] FLAGS 0 [30] FLAGS_1 0 [31] SUNW_STRPAD 0x200 [32] SUNW_LDMACH EM_386 [33] PLTGOT 0x8060a5c
Well, let's re-link our application with the correct directory specified
to -R
trond@opensolaris> cc -o myprog main.o -L. -R/home/trond/bin -lfoo trond@opensolaris> cp myprog /home/trond/bin trond@opensolaris> myprog The value is 1
This doesn't feel very user-friendly.. I have hard-coded in my binary where
it should search for it's shared libraries. If all users install the
binaries in the standard directories such as /opt/foo/bin/myfoo
it would work, but it doesn't seem flexible. Luckily for us the run-time linker
in Solaris may help us out here, so that we can instruct the runtime linker
to search for shared libraries relative to where the binary is installed.
If we use the special token $ORIGIN the runtime linker will
replace that with the location of the binary. So let's relink our application
once more, but this time we want to move the library to $HOME/lib,
and keep the binary in $HOME/bin.
trond@opensolaris> mv /home/trond/bin/libfoo.so /home/trond/lib trond@opensolaris> cc -o myprog main.o -L. -R\$ORIGIN/../lib -lfoo trond@opensolaris> cp myprog /home/trond/bin trond@opensolaris> myprog The value is 1
So let's use truss to see what's happening :-)
trond@opensolaris> truss /home/trond/bin/myprog
execve("/home/trond/bin/myprog", 0x08047708, 0x08047710) argc = 1
mmap(0x00000000, 4096, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANON, -1, 0) = 0xFEFB0000
resolvepath("/usr/lib/ld.so.1", "/lib/ld.so.1", 1023) = 12
resolvepath("/home/trond/bin/myprog", "/home/trond/bin/myprog", 1023) = 25
stat64("/home/trond/bin/myprog", 0x0804732C) = 0
open("/var/ld/ld.config", O_RDONLY) Err#2 ENOENT
sysconfig(_CONFIG_PAGESIZE) = 4096
stat64("/home/trond/bin/../lib/libfoo.so", 0x08046B0C) = 0
resolvepath("/home/trond/bin/../lib/libfoo.so", "/home/trond/lib/libfoo.so", 1023) = 28
open("/home/trond/bin/../lib/libfoo.so", O_RDONLY) = 3
mmap(0x00010000, 32768, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_ALIGN, 3, 0) = 0xFEFA0000
mmap(0x00010000, 69632, PROT_NONE, MAP_PRIVATE|MAP_NORESERVE|MAP_ANON|MAP_ALIGN, -1, 0) = 0xFEF80000
mmap(0xFEF80000, 1489, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_TEXT, 3, 0) = 0xFEF80000
mmap(0xFEF90000, 1812, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_INITDATA, 3, 0) = 0xFEF90000
munmap(0xFEF81000, 61440) = 0
memcntl(0xFEF80000, 1300, MC_ADVISE, MADV_WILLNEED, 0, 0) = 0
close(3) = 0
stat64("/home/trond/bin/../lib/libc.so.1", 0x08046B0C) Err#2 ENOENT
stat64("/lib/libc.so.1", 0x08046B0C) = 0
resolvepath("/lib/libc.so.1", "/lib/libc.so.1", 1023) = 14
open("/lib/libc.so.1", O_RDONLY) = 3
mmap(0xFEFA0000, 32768, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED, 3, 0) = 0xFEFA0000
mmap(0x00010000, 1409024, PROT_NONE, MAP_PRIVATE|MAP_NORESERVE|MAP_ANON|MAP_ALIGN, -1, 0) = 0xFEE20000
mmap(0xFEE20000, 1305977, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_TEXT, 3, 0) = 0xFEE20000
mmap(0xFEF6F000, 28320, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_INITDATA, 3, 1306624) = 0xFEF6F000
mmap(0xFEF76000, 6328, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_ANON, -1, 0) = 0xFEF76000
munmap(0xFEF5F000, 65536) = 0
memcntl(0xFEE20000, 188300, MC_ADVISE, MADV_WILLNEED, 0, 0) = 0
close(3) = 0
mmap(0x00010000, 24576, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANON|MAP_ALIGN, -1, 0) = 0xFEE10000
munmap(0xFEFA0000, 32768) = 0
getcontext(0x0804718C)
getrlimit(RLIMIT_STACK, 0x08047184) = 0
getpid() = 10032 [10031]
lwp_private(0, 1, 0xFEE12A00) = 0x000001C3
setustack(0xFEE12A60)
sysi86(SI86FPSTART, 0xFEF76FCC, 0x0000133F, 0x00001F80) = 0x00000001
ioctl(1, TCGETA, 0x080467A0) = 0
fstat64(1, 0x08046700) = 0
The value is 1
write(1, " T h e v a l u e i s".., 15) = 15
_exit(0)
In the beginning of the blog I said that one of the benefits of using shared objects was that we didn't have to relink our application if we found a bug in the library we needed to fix, so I thought we should show that as well. So let's modify the library:
trond@opensolaris> cat lib.c
#include <stdio.h>
void my_print(int val) {
fprintf(stdout, "The end...\n");
}
trond@opensolaris> cc -c -Kpic lib.c
trond@opensolaris> cc -o libfoo.so -G lib.o
trond@opensolaris> cp libfoo.so /home/trond/lib
trond@opensolaris> myprog
The end!!!
So that's it for this time :-)
Posted at 03:23PM Feb 25, 2009 by trond in OpenSolaris | Comments[0]
Tuesday February 24, 2009
coreadm
If your program tries to execute an illegal instruction (or receives an unexpected signal), Solaris will terminate the program and write a dump of the program image and state into a file named core in the current directory of the running application (just like any other Unix out there). This sounds great, but there are some caveats:
/" as the current directory. "/" should not be writable for normal users, and you don't want all your daemons running as root. This means that your daemons cannot dump core :-(
To solve this (and other problems) Solaris provides a tool named coreadm that allows you to tune the behavior to fit your needs :-)
To set the core filename to “core.program-name.pid” for all programs started from this shell, just execute the following command:
trond@opensolaris> coreadm -p “core.%f.%p” $$
You may set this as the default corefile name by executing the following command:
trond@opensolaris> pfexec coreadm -i "core.%f.%p"
Check out the documentation for a full description and examples for what you may do with coreadm.
Posted at 09:36AM Feb 24, 2009 by trond in OpenSolaris | Comments[0]
Thursday February 19, 2009
Using OpenSolaris as a development platform
OpenSolaris is a great platform for software development. OpenSolaris is like a Swiss army knife, it contains a lot of great tools that will make your life as a developer much easier. In my previous job I worked on a cross-platform product, so I had to develop and debug the application on a wide range of platforms (Solaris, Trusted Solaris, Linux, Microsoft Windows, SCO, HP-UX). All of the platforms provide a set of debugging tools with its strengths and weaknesses, bug when it comes to trying to nail down a bug out in the customers datacenter none of the other environments could compare to Solaris. The only problem is that you need to know of the existence of the tool before you can use it! I have been using Solaris (and now OpenSolaris) as my primary developent platform for a lot of years now, so I thought I would start a little serie of blogs covering different tools available in OpenSolaris.
You will find more information about all the tools I am going to mention in the manual page for the tool, and http://docs.sun.com contains a lot of information. If I could give you just one advice, it would be: "Know your tools". If you know how to use your tools and what they are capable of, you know how to choose the right tool is to solve your problem!
I am running OpenSolaris on my development machines, so I will create my examples and description on OpenSolaris. You will also find most of the tools in Solaris, but the installation procedure (and installed location) may be different. If you want to try them yourself I would recommend installing OpenSolaris 2008.11, and if you don't have a real computer to install it on, you could just download VirtualBox and install it a virtual machine.
A default installation of OpenSolaris does not install development tools, so the first thing we have to do is to install all of the software development tools (compiler, debugger, SCM systems, autoconf etc). Luckily for us there is a package containing all of the tools we need: ss-dev. So let's start by installing the Sun Studio developent package:
trond@opensolaris:~$ pfexec pkg install ss-dev
But hang on a second, why not just use gcc and gdb? After all, developers stick with the tools they are used to. There are a lot of developers out there that still thinks the combination of vi and make is the killer combination :-)
For me the answer is easy:
A lot of developers like to do their development inside an IDE, and you start Sun Studio from the application menu, or by typing sunstudio on the command line:
trond@opensolaris:~$ sunstudio
You can of course continue to develop with vi/emacs/gedit/... and make if want. If you are going to write your own configure-scripts or Makefiles, I guess you will find the following link containing a nice translation of the command-line options for the gcc compiler to the Sun Studio compiler handy: http://developers.sun.com/sunstudio/documentation/techart/gc_options.html#Compiler_Linker_Option_Translati
Posted at 09:46PM Feb 19, 2009 by trond in OpenSolaris | Comments[2]
Wednesday February 11, 2009
Enable corefiles in Drizzle
In my previous blog post I created a SMF manifest to let you manage Drizzle like every other service running on your system. For some odd reason Drizzle will catch all signals and exit cleanly instead of creating a core dump, making finding bugs much harder (who wants to read logfiles instead of poking around in the corefile with a debugger?). I was thinking of filing a bug for this behavior until Monty Taylor pointed out to me that there is a command line option I may add to enable creations of core dumps.
With this new information I think we should modify the configuration I created in the previous blog post. In the previous blog post we stored the database in /var/drizzle. I would like to move the database to /var/drizzle/data, and store all corefiles in /var/drizzle/crash. If you want to preserve your old database you should move the files in /var/drizzle/data (or if you don't care you can just go ahead and delete them)
trond@opensolaris:~$ svcadm disable drizzle trond@opensolaris:~$ svccfg delete drizzle trond@opensolaris:~$ pfexec mkdir /var/drizzle/data /var/drizzle/crash trond@opensolaris:~$ pfexec chown -R drizzle:drizzle /var/drizzle
The next thing we should do is to modify the SMF manifest so that we may modify the corefile pattern later on:
<?xml version="1.0"?>
<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
<service_bundle type="manifest" name="drizzle">
<service name="application/database/drizzle" type="service" version="1">
<single_instance/>
<dependency name="multi-user-server" grouping="require_all" restart_on="none" type="service">
<service_fmri value="svc:/milestone/multi-user-server" />
</dependency>
<property_group name="general" type="framework">
<propval name="action_authorization" type="astring"
value="solaris.smf.manage.drizzle" />
<propval name="value_authorization" type="astring"
value="solaris.smf.value.drizzle" />
</property_group>
<property_group name="drizzle" type="application">
<propval name="datadir" type="astring"
value="/var/drizzle/data" />
<propval name="corepattern" type="astring"
value="/var/drizzle/crash/core.%f.%p" />
<propval name="port" type="astring"
value="4427" />
</property_group>
<instance name="drizzle" enabled="false">
<exec_method type="method" name="start" exec="/lib/svc/method/drizzle start" timeout_seconds="30" >
<method_context>
<method_credential user="drizzle" group="drizzle" />
</method_context>
</exec_method>
<exec_method type="method" name="stop" exec="/lib/svc/method/drizzle stop %{restarter/contract}" timeout_seconds="60" >
<method_context>
<method_credential user="drizzle" group="drizzle" />
</method_context>
</exec_method>
</instance>
<stability value="Unstable" />
<template>
<common_name>
<loctext xml:lang="C">Drizzle database server</loctext>
</common_name>
<documentation>
<manpage title="drizzle" section="1" manpath="/opt/drizzle/share/man" />
</documentation>
</template>
</service>
</service_bundle>
With the new configuration option corepattern specified, we can modify the startup script:
#!/sbin/sh
. /lib/svc/share/smf_include.sh
case "$1" in
'start')
coreadm -p "`svcprop -p drizzle/corepattern $SMF_FMRI`" $$
/opt/drizzle/sbin/drizzled --datadir=`svcprop -p drizzle/datadir $SMF_FMRI` --port=`svcprop -p drizzle/port $SMF_FMRI` --skip-stack-trace &
;;
'stop')
smf_kill_contract $2 TERM 1
;;
*)
echo "Usage: $0 {start|stop}"
exit 1
;;
esac
exit $SMF_EXIT_OK
To install the files and create the SMF service, execute the following commands:
trond@opensolaris:~$ pfexec install -f /lib/svc/method drizzle trond@opensolaris:~$ pfexec install -f /var/svc/manifest/application -m 0444 drizzle.xml trond@opensolaris:~$ svccfg import /var/svc/manifest/application/drizzle.xml trond@opensolaris:~$ svccfg enable drizzle
If you try to kill the drizzled process with signal 6 (SIGABORT) you should see a corefile in /var/drizzle/crash:
trond@opensolaris:~$ pfexec pkill -6 -x drizzled trond@opensolaris:~$ pfexec ls -l /var/drizzle/crash total 65823 -rw------- 1 drizzle drizzle 67312364 2009-02-11 22:05 core.drizzled.2189
Posted at 10:07PM Feb 11, 2009 by trond in OpenSolaris | Comments[2]
Saturday February 07, 2009
Manage Drizzle with SMF
In my previous post I described how easy it is to compile Drizzle on OpenSolaris. Most people don't just compile software for fun (there are some geeks like me out there that actually do that..), people want to use the software. In this blog I will show you how to integrate Drizzle with SMF, so that you may administer drizzle just like any other service.
I will continue to use the VirtualBox image I created in the previous blog post, but I like to run the latest bits of OpenSolaris so we start by installing the latest development version. To do this we need to change the location of our preferred package repository:
trond@opensolaris:~$ pfexec pkg set-authority -O http://pkg.opensolaris.org/dev/ opensolaris.org
With the new package repository in place, we can upgrade to the latest bits with the following commands:
trond@opensolaris:~$ pfexec pkg install SUNWipkg trond@opensolaris:~$ pfexec pkg image-update trond@opensolaris:~$ pfexec init 6
The last thing we did in the previous blog post was running "gmake install" to install the binaries into /opt/drizzle. gmake install created a user named drizzle, but we want to run drizzle as a role. I guess that there is a command that may convert a user into a role, but I normally just delete user and recreate it as a role with the same user id:
trond@opensolaris:~$ id drizzle uid=102(drizzle) gid=100(drizzle) groups=100(drizzle) trond@opensolaris:~$ pfexec userdel drizzle trond@opensolaris:~$ pfexec roleadd -d /opt/drizzle/var -g drizzle -u 102 -s /bin/bash drizzle
We don't want to store our database in /opt/drizzle/var, so let's go ahead and create /var/drizzle instead:
trond@opensolaris:~$ pfexec zfs create -o mountpoint=/var/drizzle rpool/drizzle-data trond@opensolaris:~$ pfexec chown drizzle:drizzle /var/drizzle trond@opensolaris:~$ pfexec chmod 0700 /var/drizzle trond@opensolaris:~$ pfexec rmdir /opt/drizzle/var
We need to create authorizations in order to let normal users able to start and stop Drizzle through SMF. We create two new authorizations: One to change the configuration variables for our Drizzle service, and one for managing (start / stop) the service. Use pfexec vi /etc/security/auth_attr to add the following lines:
solaris.smf.value.drizzle:::Change Drizzle value properties:: solaris.smf.manage.drizzle:::Manage Drizzle service states::
To make it easier for ourselves, we can also create a new profile containing these authorizations. Use pfexec vi /etc/security/prof_attr to add the following line:
Drizzle Administration::::auths=solaris.smf.manage.drizzle,solaris.smf.value.drizzle
With the authorizations and profile defined, it is time to create the SMF descriptor. The SMF descriptor is an XML file describing our service. I will not comment every everything here, but just the interesting sections. You should store the following in a file named drizzle.xml
<?xml version="1.0"?>
<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
<service_bundle type="manifest" name="drizzle">
<service name="application/database/drizzle" type="service" version="1">
<single_instance/>
<dependency name="multi-user-server" grouping="require_all" restart_on="none" type="service">
<service_fmri value="svc:/milestone/multi-user-server" />
</dependency>
<!-- We need to map the name of the authorizations we defined to this service -->
<property_group name="general" type="framework">
<propval name="action_authorization" type="astring"
value="solaris.smf.manage.drizzle" />
<propval name="value_authorization" type="astring"
value="solaris.smf.value.drizzle" />
</property_group>
<property_group name="drizzle" type="application">
<propval name="datadir" type="astring"
value="/var/drizzle" />
<propval name="port" type="astring"
value="4427" />
</property_group>
<!-- Define the instance and how to start / stop it -->
<instance name="drizzle" enabled="false">
<exec_method type="method" name="start" exec="/lib/svc/method/drizzle start" timeout_seconds="30" >
<method_context>
<method_credential user="drizzle" group="drizzle" />
</method_context>
</exec_method>
<exec_method type="method" name="stop" exec="/lib/svc/method/drizzle stop %{restarter/contract}" timeout_seconds="60" >
<method_context>
<method_credential user="drizzle" group="drizzle" />
</method_context>
</exec_method>
</instance>
<stability value="Unstable" />
<template>
<common_name>
<loctext xml:lang="C">Drizzle database server</loctext>
</common_name>
<documentation>
<manpage title="drizzle" section="1" manpath="/opt/drizzle/share/man" />
</documentation>
</template>
</service>
</service_bundle>
As you see above we execute the script /lib/svc/method/drizzle to start or stop the server. Save the following lines into a file named drizzle:
#!/sbin/sh
. /lib/svc/share/smf_include.sh
case "$1" in
'start')
/opt/drizzle/sbin/drizzled --datadir=`svcprop -p drizzle/datadir $SMF_FMRI` --port=`svcprop -p drizzle/port $SMF_FMRI` &
;;
'stop')
smf_kill_contract $2 TERM 1
;;
*)
echo "Usage: $0 {start|stop}"
exit 1
;;
esac
exit $SMF_EXIT_OK
To install the files and create the SMF service, execute the following commands:
trond@opensolaris:~$ pfexec install -f /lib/svc/method drizzle trond@opensolaris:~$ pfexec install -f /var/svc/manifest/application -m 0444 drizzle.xml trond@opensolaris:~$ svccfg import /var/svc/manifest/application/drizzle.xml
Well that's it. You can now manage your Drizzle server as every other service managed by SMF:
trond@opensolaris:~$ svcs drizzle STATE STIME FMRI disabled 20:03:55 svc:/application/database/drizzle:drizzle trond@opensolaris:~$ svcadm enable drizzle trond@opensolaris:~$ svcs drizzle STATE STIME FMRI online 20:04:07 svc:/application/database/drizzle:drizzle trond@opensolaris:~$ ps -ef |grep drizzle drizzle 1260 1 1 20:04:08 ? 0:00 /opt/drizzle/sbin/drizzled --datadir=/var/drizzle --port=4427
We can modify the configuration (data dir and port number) through svccfg:
trond@opensolaris:~$ svccfg svc:> select drizzle svc:/application/database/drizzle> listprop general framework general/action_authorization astring solaris.smf.manage.drizzle general/entity_stability astring Unstable general/single_instance boolean true general/value_authorization astring solaris.smf.value.drizzle multi-user-server dependency multi-user-server/entities fmri svc:/milestone/multi-user-server multi-user-server/grouping astring require_all multi-user-server/restart_on astring none multi-user-server/type astring service drizzle application drizzle/datadir astring /var/drizzle drizzle/port astring 4427 tm_common_name template tm_common_name/C ustring "Drizzle database server" tm_man_drizzle template tm_man_drizzle/manpath astring /opt/drizzle/share/man tm_man_drizzle/section astring 1 tm_man_drizzle/title astring drizzle svc:/application/database/drizzle> setprop drizzle/port=9999 svc:/application/database/drizzle> quit trond@opensolaris:~$ svcadm refresh drizzle trond@opensolaris:~$ svcadm restart drizzle trond@opensolaris:~$ ps -ef | grep drizzle drizzle 1301 1 1 20:07:52 ? 0:00 /opt/drizzle/sbin/drizzled --datadir=/var/drizzle --port=9999
Posted at 08:19PM Feb 07, 2009 by trond in OpenSolaris | Comments[0]
Friday February 06, 2009
Compiling Drizzle on OpenSolaris
Drizzle is certainly in the wind these days. I hear people talking about it almost every day (guess I got geeky friends ;-)), so I decided I should write a small blog on how easy it is to build Drizzle on my pet; OpenSolaris.
To ensure that I don't miss a step in this guide, I decided to install a fresh installation of OpenSolaris 2008.11. With VirtualBox available I didn't have to reinstall one of my computers :-)
A default installation of OpenSolaris does not install everything you need to compile Drizzle (that wouldn't fit on a cd), and most normal users doesn't need development tools. Luckily you don't have to be a senior system administrator to install software on OpenSolaris.
So let's go ahead and install the dependencies we need:
trond@opensolaris:~$ pfexec pkg install ss-dev trond@opensolaris:~$ pfexec pkg install SUNWgnu-gperf trond@opensolaris:~$ pfexec pkg install SUNWlibevent
The pcre in OpenSolaris is newer than the one you normally find in other distributions, and in the new versions of pcre they have moved the headerfile pcre.h to pcre/pcre.h. Drizzle is not updated to look for the new header, so until that is fixed in Drizzle we can just use the following workaround:
trond@opensolaris:~$ pfexec ln -s pcre/pcre.h /usr/include/pcre.h
GNU readline support is mandatory in Drizzle, but the readline package for OpenSolaris is not available in the default repositories. The following commands will add another repository you may install packages from, and install the readline library:
trond@opensolaris:~$ pfexec pkg set-authority -O http://pkg.opensolaris.org/pending/ pending trond@opensolaris:~$ pfexec pkg install readline5
Drizzle use Google Protocol buffers, and they are not available as an installable software package so we need to download and compile it ourself:
trond@opensolaris:~$ mkdir download && cd download trond@opensolaris:~/download$ wget http://protobuf.googlecode.com/files/protobuf-2.0.3.tar.gz trond@opensolaris:~/download$ tar xfz protobuf-2.0.3.tar.gz trond@opensolaris:~/download$ cd protobuf-2.0.3 trond@opensolaris:~/download/protobuf-2.0.3$ ./configure --disable-static --prefix=/usr trond@opensolaris:~/download/protobuf-2.0.3$ gmake trond@opensolaris:~/download/protobuf-2.0.3$ pfexec gmake install
Finally we need to download and compile Bazaar to check out the source code (unless you want to download a tar-ball somewhere)
trond@opensolaris:~/download$ wget --no-check-certificate https://launchpad.net/bzr/1.11/1.11/+download/bzr-1.11.tar.gz trond@opensolaris:~/download$ tar xfz bzr-1.11.tar.gz trond@opensolaris:~/download$ cd bzr-1.11 trond@opensolaris:~/download/bzr-1.11$ pfexec python setup.py install
Well, that's it. Now we just need to check out the source gode and compile it:
trond@opensolaris:~$ mkdir source && cd source trond@opensolaris:~/source$ bzr branch lp:drizzle trond@opensolaris:~/source$ cd drizzle trond@opensolaris:~/source/drizzle$ ./config/autorun.sh trond@opensolaris:~/source/drizzle$ ./configure --prefix=/opt/drizzle trond@opensolaris:~/source/drizzle$ gmake all trond@opensolaris:~/source/drizzle$ pfexec gmake install
Posted at 11:17PM Feb 06, 2009 by trond in OpenSolaris | Comments[2]