Set up Haskell development environment on Arch Linux

Follow this post, install stack first:

# pacman -S stack
resolving dependencies...
looking for conflicting packages...
......

At the end of installation, it prompts following words:

You need to either 1) install latest stable ghc package from [extra] or 2) install ncurses5compat-libs from AUR for the prebuilt binaries installed by stack to work.

So install ghc further:

# pacman -S ghc
resolving dependencies...
looking for conflicting packages...

Now that the environment is ready, you should modify your own ~/.stack/config.yaml file:

# This file contains default non-project-specific settings for 'stack', used
# in all projects.  For more information about stack's configuration, see
# http://docs.haskellstack.org/en/stable/yaml_configuration/

# The following parameters are used by "stack new" to automatically fill fields
# in the cabal config. We recommend uncommenting them and filling them out if
# you intend to use 'stack new'.
# See https://docs.haskellstack.org/en/stable/yaml_configuration/#templates
templates:
  params:
#    author-name:
#    author-email:
#    copyright:
#    github-username:

For example, add name, email etc.

Then follow this link to create a simple program:

# stack new hello
# cd hello
# stack setup
# stack build
# stack exec hello-exe

You will see “someFunc” is outputted.

BTW, you can also use ghc compiler directly. E.g., write a “Hello world” program (Reference is here):

# cat hello.hs
main :: IO ()
main = putStrLn "Hello World!"
# ghc -dynamic hello.hs
# ./hello
Hello World!

That’s all!

Be careful of thread stack size

Today, my colleague came across a thread stack overflow core dump:

Capture

From above diagram, we can see only this function’s stack will occupy ~7 MiB space. Check the stack size configuration on system:

$ ulimit -S -s
8192

just 8 MiB. Double the stack size:

$ ulimit -S -s 16384

The program won’t crash.

Reference:
General: How do I change my default limits for stack size, core file size, etc.?.

 

Fix locale configuration issue on Arch Linux

On a new installed Arch Linux server, I come across the following problem:

# locale -a
locale: Cannot set LC_CTYPE to default locale: No such file or directory
locale: Cannot set LC_MESSAGES to default locale: No such file or directory
locale: Cannot set LC_COLLATE to default locale: No such file or directory
C
POSIX
# locale
locale: Cannot set LC_CTYPE to default locale: No such file or directory
locale: Cannot set LC_MESSAGES to default locale: No such file or directory
locale: Cannot set LC_ALL to default locale: No such file or directory
LANG=en_US.UTF-8
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_PAPER="en_US.UTF-8"
LC_NAME="en_US.UTF-8"
LC_ADDRESS="en_US.UTF-8"
LC_TELEPHONE="en_US.UTF-8"
LC_MEASUREMENT="en_US.UTF-8"
LC_IDENTIFICATION="en_US.UTF-8"
LC_ALL=

Especially when using man command:

# man wait
man: can't set the locale; make sure $LC_* and $LANG are correct

After referring this post, I fix the issue. My /etc/locale.conf is like this:

# cat /etc/locale.conf
LANG=en_US.UTF-8

But en_US.UTF-8 is commented out in /etc/locale.gen:

#en_US.UTF-8 UTF-8

uncomment it, then run locale-gen command, all become normal.

The anatomy of tee program on OpenBSD

The tee command is used to read content from standard input and displays it not only in standard output but also saves to other files simultaneously. The source code of tee in OpenBSD is very simple, and I want to give it an analysis:

(1) tee leverages Singlely-linked List defined in sys/queue.h to manage outputted files (including standard output):

struct list {
    SLIST_ENTRY(list) next;
    int fd;
    char *name;
};
SLIST_HEAD(, list) head;

......

static void
add(int fd, char *name)
{
    struct list *p;
    ......
    SLIST_INSERT_HEAD(&head, p, next);
}

int
main(int argc, char *argv[])
{
    struct list *p;
    ......
    SLIST_INIT(&head);
    ......
    SLIST_FOREACH(p, &head, next) {
        ......
    }
}

To understand it easily, I extract the macros from sys/queue.h and created a file which utilizes the marcos:

#define SLIST_HEAD(name, type)                      \
struct name {                               \
    struct type *slh_first; /* first element */         \
}

#define SLIST_ENTRY(type)                       \
struct {                                \
    struct type *sle_next;  /* next element */          \
}

#define SLIST_FIRST(head)   ((head)->slh_first)
#define SLIST_END(head)     NULL
#define SLIST_EMPTY(head)   (SLIST_FIRST(head) == SLIST_END(head))
#define SLIST_NEXT(elm, field)  ((elm)->field.sle_next)

#define SLIST_FOREACH(var, head, field)                 \
    for((var) = SLIST_FIRST(head);                  \
        (var) != SLIST_END(head);                   \
        (var) = SLIST_NEXT(var, field))

#define SLIST_INIT(head) {                      \
    SLIST_FIRST(head) = SLIST_END(head);                \
}

#define SLIST_INSERT_HEAD(head, elm, field) do {            \
    (elm)->field.sle_next = (head)->slh_first;          \
    (head)->slh_first = (elm);                  \
} while (0)

struct list {
    SLIST_ENTRY(list) next;
    int fd;
    char *name;
};
SLIST_HEAD(, list) head;

int
main(int argc, char *argv[])
{
    struct list *p;
    SLIST_INIT(&head);

    SLIST_INSERT_HEAD(&head, p, next);
    SLIST_FOREACH(p, &head, next) {

    }
}

Then employed gcc‘s pre-processing function:

# gcc -E slist.c
# 1 "slist.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "slist.c"
# 30 "slist.c"
struct list {
 struct { struct list *sle_next; } next;
 int fd;
 char *name;
};
struct { struct list *slh_first; } head;

int
main(int argc, char *argv[])
{
 struct list *p;
 { ((&head)->slh_first) = NULL; };

 do { (p)->next.sle_next = (&head)->slh_first; (&head)->slh_first = (p); } while (0);
 for((p) = ((&head)->slh_first); (p) != NULL; (p) = ((p)->next.sle_next)) {

 }
}

It becomes clear now! The head node in list contains only 1 member: slh_first, which points to the first valid node. For the elements in the list, it is embedded with next struct which uses sle_next to refer to next buddy.

(2) By default, tee will overwrite the output files. If you want to append it, use -a option, and the code is as following:

while (*argv) {
    if ((fd = open(*argv, O_WRONLY | O_CREAT |
        (append ? O_APPEND : O_TRUNC), DEFFILEMODE)) == -1) {
        ......
    } 
    ......
}

(3) The next part is the skeleton of saving content to files:

while ((rval = read(STDIN_FILENO, buf, sizeof(buf))) > 0) {
    SLIST_FOREACH(p, &head, next) {
        n = rval;
        bp = buf;
        do {
            if ((wval = write(p->fd, bp, n)) == -1) {
                ......
            }
            bp += wval;
        } while (n -= wval);
    }
}

We need to iterates every opened file descriptor and write contents into it.

(4) Normally, theinterrupt signal will cause tee exit:

# tee
fdkfkdfjk
fdkfkdfjk
^C
#

To disable this feature, use -i option:

# tee -i
fdhfhd
fdhfhd
^C^C

The corresponding code is like this:

......
case 'i':
    (void)signal(SIGINT, SIG_IGN);
    break;

Build SEAL on Linux

Building SEAL(Simple Encrypted Arithmetic Library) on Linux needs some tweaks:

(1) Add executable attribute for configure file:

$ chmod a+x configure

(2) Running configure generates following errors:

$ ./configure
-bash: ./configure: /bin/sh^M: bad interpreter: No such file or directory

dos2unix doesn’t take effect too:

$ dos2unix configure
dos2unix: Binary symbol 0x07 found at line 3911
dos2unix: Skipping binary file configure

Need tr to save me:

$ tr -d '\r' < configure > temp
$ mv temp configure

Then compiling is OK:

$ ./configure
$ make