2013/07/31


個人的にお借りしているサーバがあってそこで何個かbotを動かしているのだけど、そのサーバがセキュリティアップデート等で再起動した後、ログインしてscreen起動して、その中で画面割ってbot起動して、また別の画面でirssiを起動する、みたいな事を毎回やってた訳ですがいい加減めんどう臭くなってきたので自動化した。 まずscreenを自動起動する仕組みを考えた。rcスクリプトでもいいけど、そもそも共用サーバなのでroot権限が無い。そこでcronを使う。crontab -e して @reboot (. ~/.profile; /usr/bin/screen -d -m)
@reboot という識別を使います。再起動して1回だけ実行されるコマンドが書けます。最近の linux であれば使えるかと思います。
ここで .profile を読み込んでるのは、これをしないと screen が新しく起動するプロセスに対してインタラクティブシェルから起動していたはずの環境変数等を引き継いでくれないから。例えば最近の Ubuntu であれば ~/.bashrc # If not running interactively, don't do anything
case $- in
    *i*) ;;
      *) return;;
esac
こんな風にインタラクティブシェルじゃない場合には読み込まない様になっている。編集してもいいんだけど、僕はどの環境でも使えるベストプラクティスを選んだ。
また screen に渡している -d -m、これが無いとコンソールと通信するはずの screen が端末にアクセス出来なくて死ぬ。起動してすぐデタッチしてます。

あとは ~/.screenrc で好きなように画面を割って各プロセスを起動すれば良い。 startup_message off
vbell off
defshell /bin/bash
defscrollback 1000
hardstatus string "%?%H %?[screen %n%?: %t%?] %h"
screen 0 irssi
screen 1 /home/mattn/bin/startup-servers
僕の場合は、goreman というツールでサーバやbotを起動していて、それと irssi で画面分割している。
ちなみに defshell を設定しないと cron から起動した場合は $SHELL が設定されていないので、/bin/sh になってしまうのを回避する為に必要です。
Posted at by



2013/07/28


Go言語は例外発生時にdeferを使いrecover()を呼び出す手法で例外補足する事ができ、よくこれは「特殊なので分かり辛い」とか「try/catch/finallyを実装しない理由」と言われる事があります。
確かに一般的な例外補足と比べるとチープに見えたりもします。
なんとなく、以前書いたjsdeferredのGo言語版godeferredを使えば出来るんじゃないかと思ったので書いてみた。
mattn/go-try - GitHub

try/catch/finally for go

https://github.com/mattn/go-try
これを使うとこんな感じに書けます。 Try(func() {
    panic(1)
}).Catch(func(n int) {
    println("int exception:", n)
})
制御構文ではないので、メソッドチェインを使って実現しています。実際にはTry関数からCatchメソッドもしくはFinallyメソッドしか呼び出せないCatchOrFinallyオブジェクトを返しています。CatchメソッドはまたCatchOrFinallyオブジェクトを返すという仕組みですね。いわゆるMaybeモナドといった所でしょうか。
int例外だけでなく Try(func() {
    panic("foo")
}).Catch(func(s string) {
    println("string exception:", s)
})
string例外や他の型も扱えます。またpanicの例外補足だけでなく package main

import (
    . "github.com/mattn/go-try/try"
    "fmt"
)

func main() {
    Try(func() {
        v := 0
        println(1 / v)
    }).Catch(func(n int) {
        // not pass
        println("Caught int exception:", n)
    }).Catch(func(s string) {
        // not pass
        println("Caught string exception:", s)
    }).Catch(func(e RuntimeError) {
        fmt.Println("Caught runtime exception:", e)
        for _, st := range e.StackTrace {
            fmt.Printf("  %s:%d\n", st.File, st.Line)
        }
    }).Finally(func() {
        println("finalize")
    })
}
この様なランタイムエラーも補足出きるようになっていて、RuntimeErrorオブジェクトのStackTraceフィールドからスタックトレースも参照出来ます。
ただ残念ながらスタックトレースの行番号は、関数呼び出し位置になるので、上記の例でいうとTryブロックのどの位置で例外が発生してもTry関数の呼び出し位置になってしまっています。
追記
修正しました。こんな感じに出力されます。
Caught runtime exception: runtime error: integer divide by zero
  /home/mattn/dev/go-try/try/foo.go:11
  /home/mattn/dev/go/src/pkg/reflect/value.go:583
  /home/mattn/dev/go/src/pkg/reflect/value.go:433
  /home/mattn/dev/go-try/try/try.go:51
  /home/mattn/dev/go-try/try/foo.go:15
  /home/mattn/dev/go/src/pkg/runtime/386/asm.s:94
  /home/mattn/dev/go/src/pkg/runtime/proc.c:244
finalize

まぁGo言語でちょっとリッチな書き方したいなって人向けです。
# 誰だよそれ

追記
個人的にはネタなので、あまり使わない方が良い気はします。
Posted at by



2013/07/10


おなじみC/C++から使えるJSONライブラリを紹介するコーナー。まずは過去のまとめ。
最近は結構 matsuu さんのブクマから見つけて記事を書いてたけど今日はそうじゃない所からご紹介。
Jansson — C library for working with JSON data

Jansson Jansson is a C library for encoding, decoding and manipulating JSON data. It features: Simpl...

http://www.digip.org/jansson/
特徴としては
  • 簡単で直感的な API とデータモデル
  • 包括的なドキュメント
  • 他のライブラリへの依存がない
  • ユニコードのフルサポート (UTF-8)
  • 徹底したテストスーツ
こんな感じ。いつもの様にサンプル。Twitter API がバージョン 1.1 になって認証無しでは殆ど機能しなくなったので github API を使う。 #include <assert.h>
#include <string.h>
#include <memory.h>
#include <curl/curl.h>
#include <jansson.h>

typedef struct {
  char* data;   // response data from server
  size_t size;  // response size of data
} MEMFILE;

MEMFILE*
memfopen() {
  MEMFILE* mf = (MEMFILE*) malloc(sizeof(MEMFILE));
  if (mf) {
    mf->data = NULL;
    mf->size = 0;
  }
  return mf;
}

void
memfclose(MEMFILE* mf) {
  if (mf->data) free(mf->data);
  free(mf);
}

size_t
memfwrite(char* ptr, size_t size, size_t nmemb, void* stream) {
  MEMFILE* mf = (MEMFILE*) stream;
  int block = size * nmemb;
  if (!mf) return block; // through
  if (!mf->data)
    mf->data = (char*) malloc(block);
  else
    mf->data = (char*) realloc(mf->data, mf->size + block);
  if (mf->data) {
    memcpy(mf->data + mf->size, ptr, block);
    mf->size += block;
  }
  return block;
}

char*
memfstrdup(MEMFILE* mf) {
  char* buf;
  if (mf->size == 0return NULL;
  buf = (char*) malloc(mf->size + 1);
  memcpy(buf, mf->data, mf->size);
  buf[mf->size] = 0;
  return buf;
}

int
main() {
  CURL* curl;
  MEMFILE* mf = NULL;
  char* js = NULL;
  int i;

  mf = memfopen();

  curl = curl_easy_init();
  curl_easy_setopt(curl, CURLOPT_URL, "https://api.github.com/legacy/repos/search/unko");
  curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
  curl_easy_setopt(curl, CURLOPT_WRITEDATA, mf);
  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, memfwrite);
  curl_easy_perform(curl);
  curl_easy_cleanup(curl);

  js = memfstrdup(mf);
  memfclose(mf);

  json_error_t error;
  json_t *result = json_loads(js, 0, &error);
  if (result == NULL) {
    fputs(error.text, stderr);
    goto leave;
  }
  json_t *repositories = json_object_get(result, "repositories");
  json_t *repository;
  json_array_foreach(repositories, i, repository) {
    printf("%s/%s%s\n",
      json_string_value(json_object_get(repository, "username")),
      json_string_value(json_object_get(repository, "name")),
      json_string_value(json_object_get(repository, "description")));
  }

  json_decref(result);
leave:
  free(js);
}
実行結果 mizzy/unko: テスト用
satorunet/unko: 
siyo/unko: unko
kotatsumikan/unko: ファイルの内容を「うんこ」に変換するコマンドです。
karr3304/unko: ブログシステム
t11086/unko: 勉強中
keamano/unko: うんこアプリ
nmbakfm/unko: install unko command on your PC
nanananamememe/unko: 
fivestar/unko: test
smellman/unko: まるでくそのようだ
susuhushi/unko: github
kjwtnb/unko: いろいろと書き捨て
kamiyama/xunko: unko
FromAtom/Test: Unko
tam33363/unko_wave: 
konyavic/unko-curry: 
konishika/unko_ng: 
oilfield/unko_hakken: oilfield
tanaton/unko2ch: 2ちゃんねる過去ログ転送サービス
kenmaz/cocos2d_UnkoYoke: simple unko game
mattn/Plack-Middleware-ReplaceToUnko: plack middleware for replacing images to shit image that referer from external sites.
legokichi/superUnkoChanLv200: roombaProgramming
Plavender/Unkown: 
omomuron/unkonow: 
FanPF/unkown: I don't  kown
ThievingSix/UnkosAMI: 
nakajijapan/unkore_iphone: iPhone Game - Bureau of Nakajima Cleansing
比較する物としては parson が一番近いです。parson は各JSON 型がそれぞれ別のC言語型として扱われている為、value と object/array/number/boolean/string といったデータ型の交換が必要で、parson では json_value_get_array という関数の様に value → array という型変換はもちろん、array 内要素の value から目的の型への変換が必要になったりします。
しかし jansson の場合は全て json_t 型のポインタで交換されていて、特に型交換する必要もなく、型チェックが必要な場合は json_is_array という関数でチェックが可能なのでとても直感的に、かつシームレスに処理が書けます。
そして parson の一番の欠点としてシリアライザが無い事が挙げられますが、jansson には存在します(json_dumps等)。
この点から見て、僕が把握しているC言語から使えるJSONパーサだと、現状 jansson が一番扱いやすいと思います。
ライセンスはMIT、チュートリアルが少ないですがAPIドキュメントはしっかり書いてあります。

ぜひ仕事でも使って行きたいと思います。
Posted at by