kenkovlog

Haskell, Python, Vim, ...

GRUBとデュアルブートとパーティション

GRUBのインストールと設定について。

DebianとArch Linuxデュアルブートにしようと思い、
GRUBをどのようにインストールすればいいか調べました。

パーティションは次のようになっています。

/dev/sda         MBR
/dev/sda1  boot  Debian /
/dev/sda3  boot  Arch   /
/dev/sda5        Debian Swap
/dev/sda6        Arch Swap

【手順】
1. Debianをインストール
2. Debianのインストール時にGRUBをsda(MBR)にインストールした
3. Archのインストール
4. Archのインストール時にGRUBをsda3(Archのroot)にインストールした

このようにインストールすると、起動時にはMBRが読み込まれ、
Debianをインストールする時に入れたGRUBが起動します。
そのため、何も設定をしていない場合は、ArchはGRUBの起動メニューに
表示されないことになります。

したがって次に、ArchがGRUBのメニュリストに表示されるように、
Debianを起動し、/boot/grub/menu.lstの編集を行います。

title       Debian GNU/Linux, kernel 2.6.26-2-686
root        (hd0,0)
kernel      /boot/vmlinuz-2.6.26-2-686 root=/dev/sda1 ro quiet
initrc      /boot/initrc.img-2.6.26-2-686

title       Debian GNU/Linux, kernel 2.6.26-2-686 (single-user mode)
root        (hd0,0)
kernel      /boot/mvlinuz-2.6.26-2.686 root=/dev/sda2 ro single
initrc      /boot/initrc.img-2.6.26-2-686

# ここから追加する
# Arch Linuxの設定
title       Arch Linux
root        (hd0,2)
# chainloaderでロードする
# ここを上のDebianと同様に
# kernel ..
# としてロードするとロードできず失敗する。
# おそくら、パーティションの先頭にGRUBをインストールしているため。
# したがって、GRUBをインストールした場合、
# chainloaderで起動する必要があるよう
chainloader +1

Swap領域を作るのには論理パーティションを使いました。

パティションには

* 基本パーティション(基本領域)
* 拡張パーティション(拡張領域)
  * 論理パーティション(論理領域)

があります。

基本パーティションはひとつのディスクにつき4つまで作成できます。
ディスクから直接起動するOSの起動部分はこの基本パーティションに
置かなければいけません。

拡張パーティションはひとつのディスクにひとつのみ作成可能で、
拡張パーティションの中に
論理パーティションを16まで作成できます。
論理領域は必ず連続していなければなりません。

拡張領域を使った場合は、基本領域は3つまでしか作成できなくなります。

ですので、Linuxをインストールするときに手動でパーティションをするとき、
そのパーティションを基本領域にするか、論理領域にするか決めますが、
拡張パーティションにするかどうかの選択がでないのはこのためです。
論理領域にしたら、自動でその論理領域が拡張領域に含まれていると
みなされます。

上の例では

-----------------------
MBR
/dev/sda    MBR
-----------------------
基本領域
/dev/sda1   Debian /
-----------------------
基本領域
/dev/sda3   Arch   /
-----------------------
拡張領域
/dev/sda5   Debian Swap
/dev/sda6   Arch Swap

となっています。

【Archの疑問点】
ArchLinuxでGRUBをインストールする時、
インストール先として

/dev/sda
/dev/sda
/dev/sda1
/dev/sda3
/dev/sda5
/dev/sda6

とインストール先が出てきました。
このとき、もしMBRにGRUBをインストールしようとすると
上から二番目の/dev/sdaにインストールしなければいけないようです。
なぜ、上の二つに/dev/sdaと同じものが出てきているのか謎です。。

なんとなく、
https://wiki.archlinux.org/index.php/Beginners%27_Guide#.2Fetc.2Fpacman.d.2Fmirrorlist
のページに書いてある

Warning: 
Make sure to install GRUB on /dev/sdX and not /dev/sdX#! 
This is a common mistake.

が関係ある気がしますが、sda#とは書かれていなかったので、どうなっているのか解りません

Xmonadの設定

XFCEからXmonadに乗り換えました!
設定をまとめました。
参考にしたページは
http://haskell.org/haskellwiki/Xmonad/Config_archive/John_Goerzen%27s_Configuration
です。

設定ファイルの説明

メインの設定ファイル ~/.xmonad/xmonad.hs
xmobarrc設定ファイル ~/.xmobarrc
icon trayの設定ファイル ~/.xinitrc

次に各々の設定ファイルを見て行きます。

~/.xmonad/xmonad.hs

    -- ~/.xmonad/xmonad.hs

    import XMonad
    import XMonad.Hooks.DynamicLog
    import XMonad.Hooks.ManageDocks
    import XMonad.Util.Run(spawnPipe)
    import XMonad.Util.EZConfig(additionalKeys)
    import System.IO

    myManageHook = composeAll
		 -- floatさせるアプリケーション
		 [ className =? "Gimp" --> doFloat,
		   -- 次はtildaでなくTildaにしないといけない
		   className =? "Tilda" --> doFloat
		 ]

    main = do
	-- .xmobarrcの設定
	xmproc <- spawnPipe "/usr/bin/xmobar ~/.xmobarrc"
	xmonad $ defaultConfig {
	      -- terminalにはxfceのterminalを使う
	      terminal    = "terminal",
	      manageHook = manageDocks <+> myManageHook
			   <+> manageHook defaultConfig,
	      layoutHook = avoidStruts  $  layoutHook defaultConfig,
	      logHook = dynamicLogWithPP xmobarPP {
			  ppOutput = hPutStrLn xmproc,
			  ppTitle = xmobarColor "green" "" . shorten 50
			}
	      -- , modMask = modmask
	   } `additionalKeys`
	   []

~/.xmobarrc

    Config {
	font = "-misc-fixed-*-*-*-*-13-*-*-*-*-*-*-*", 
	bgColor = "black",
	fgColor = "grey",
	position = Static { xpos = 0, ypos = 0,
			    width = 1024, height = 20 },
	lowerOnStart = True
	}

~/.xinitrc

    #!/bin/sh
    #
    # ~/.xinitrc
    #
    # Executed by startx (run your window manager from here)

    # exec gnome-session
    # exec startkde
    # exec startxfce4
    # ...or the Window Manager of your choice


    ####
    #### Setting for Xmonad
    ####

    # インプットメソッドの起動
    export GTK_IM_MODULE='uim'
    export QT_IM_MODULE='uim'
    uim-xim &
    export XMODIFIERS=@im='uim'
    uim-toolbar-gtk &

    # resourceのロード
    # まだ.Xresourceを使っていないので、ここは未設定
    # xrdb -merge .Xresources

    # icon trayの設定
    trayer --edge top --align right --SetDockType true --SetPartialStrut true --expand true --width 10 --transparent true --tint 0x191970 --height 20 &

    # ccursorの設定
    xsetroot -cursor_name left_ptr

    # 背景の設定
    # xsetroot -solid midnightblue
    # 日本語キーボードの設定

    setxkbmap -layout jp

    ### アプリケーションの起動
    # スクリーンセーバの起動
    xscreensaver -no-splash &
    # nm-appletの起動
    if [ -x /usr/bin/nm-applet ] ; then
	nm-applet --sm-disable &
    fi
    # gnome-power-managerの起動
    if [ -x /usr/bin/gnome-power-manager] ; then
	gnome-power-manager
    fi
    # conkeyの起動
    conky &
    # dropboxの起動
    dropboxd &
    # tildaの起動
    tilda &

    # xmondmapの設定
    # この位置で設定しないと失敗する
    xmodmap ~/.Xmodmap

    # xmonadを起動する
    exec xmonad
    # start xmonad
    # exec ck-launch-session xmonad

EmacsのSKKの設定

昨日からEmacsSKKを使い始めました。
キーバインドで悩んだところがあったのでそのメモ

read-onlyのバッファでskkモードをONにしているとき、C-pで上に移動出来ませんでした。
-bでキーバインドを調べてみると、
C-p skk-previous-candidate
となっていました。どうやら、skkモードにすることで、マイナーモードのキーバインドになっていたようです。

-bの情報からskk.elあたりで設定されているということがわかったので、
/usr/share/emacs/site-list/skk/
の下にあるファイルにキーバインドの設定ファイルがあると思われるので、
そのあたりのファイルを探して、キーバインド
skk-vars.el
に書かれていることがわかりました。

skk-vars.elを

;; 次の行をコメントアウトして
;; (list "x" "\C-p")を(list "x")にする
;; (defcustom skk-previous-candidate-keys (list "x" "\C-p")
(defcustom skk-previous-candidate-keys (list "x")
  "*`skk-previous-candidate' を割当てるキー。
この変数にはキーを表すオブジェクトのリストを指定する。
オブジェクトとしては、キーを表す文字列または event vector が指定できる。"
  :type (if (get 'key-sequence 'widget-type)
	    '(repeat (key-sequence :tag "キー (C-q key で取得可)"))
	  '(repeat sexp))
  :group 'skk-henkan)

と修正しました。

そして、再起動...

うまくいきません。
ここで、ひとつつまづいたのが、バイトコンパイル。
skk-vars.elはすでにバイトコンパイルされているのでM-x byte-compile-fileでバイトコンパイルし直してから、Emacsを再起動することで、
C-p previout-line
となり、キーバインドの修正が出来ました。

bm.el入れました

bm.el入れましたが、ブックマークのセーブ、ロードの設定で少しつまずいたので、そのメモを。

暫定的に、今の設定ファイルはこうなっています。これでうまく動いている状況。

;;; bm.elの設定
;;; (install-elisp "http://cvs.savannah.gnu.org/viewvc/*checkout*/bm/bm/bm.el")
(require 'bm)
;; キーの設定
(global-set-key (kbd "M-@") 'bm-toggle)
(global-set-key (kbd "M-[") 'bm-previous)
(global-set-key (kbd "M-]") 'bm-next)
;; マークのセーブ
(setq-default bm-buffer-persistence t)
;; セーブファイル名の設定
(setq bm-repository-file "~/.emacs.d/.bm-repository")
;; 起動時に設定のロード
(setq bm-restore-repository-on-load t)
(add-hook 'after-init-hook 'bm-repository-load)
(add-hook 'find-file-hooks 'bm-buffer-restore)
(add-hook 'after-revert-hook 'bm-buffer-restore)
;; 設定ファイルのセーブ
(add-hook 'kill-buffer-hook 'bm-buffer-save)
(add-hook 'auto-save-hook 'bm-buffer-save)
(add-hook 'after-save-hook 'bm-buffer-save)
(add-hook 'vc-before-checkin-hook 'bm-buffer-save)
;; Saving the repository to file when on exit
;; kill-buffer-hook is not called when emacs is killed, so we
;; must save all bookmarks first
(add-hook 'kill-emacs-hook '(lambda nil
                              (bm-buffer-save-all)
                              (bm-repository-save)))

この設定は次を参考にさせていただきました。
【ぺっくブログミラー@peccul】
http://d.hatena.ne.jp/peccu/20100402

特に、最後の

;; Saving the repository to file when on exit
;; kill-buffer-hook is not called when emacs is killed, so we
;; must save all bookmarks first
(add-hook 'kill-emacs-hook '(lambda nil
                              (bm-buffer-save-all)
                              (bm-repository-save)))

の設定のところはそのままコピーさせていただいています。

Emacsコマンドメモ

最近VimからEmacsの乗り換えようと訓練してます。
目新しいコマンドがあったらメモがわりに書いていきます。

C-i  インデントする(タブと同じ)
C-m  改行する(Enterと同じ)
C-j  改行してからインデントする
C-t  文字を入れ替える
C-o  空行をあける

M-t  単語の入れ替える
M-k  現在の文を削除する
M-h  現在の段落をマークする

C-x h バッファ全体をマークする
C-x i ファイルを挿入する

一つ上の行に空行を作って移動する(VimでO)はないかなと探し中。

HaskellでOAuthとTwitter API

ゴールデンウィークの課題としていたHaskellでOAuth。
jsonの解析はまだ行えていませんが、OAuthを使ってjsonを取得するところまでできたので、その事をまとめます。

TwitterでOAuthを使う基本的な流れについてはtwitter developersのauthenticationの項
http://dev.twitter.com/pages/auth
を見てもらうことにして、ここでは各部分について実際のコードを書きます。
標準で入っていないモジュールがありますので、その際にはcabalなどを使っていれてもらえればと思います。

OAuthを使うのに一番の山場はsignatureの生成です。
僕もここでかなり苦戦しました。
Signatureの生成には次のコードを書きました。
Haskellらしくないコードの書き方かもしれませんが、
どのような手順でSignatureを生成しているのかわかるように書いています。

-- Signature.hs
-- signatureを生成する関数を提供するモジュールSignature
module Signature (
  makeSignature, 
  ConsumerKey,
  ConsumerSecret,
  AccessToken,
  AccessTokenSecret,
  URL,
  Parameter
) where

import Data.Word (Word8(..))
import Data.List
import Network.HTTP (RequestMethod, urlEncode)
-- Base64を使うために次のモジュールをインポートする
import qualified Codec.Binary.Base64 as B64
-- HMAC-SHA1を使うために次のモジュールをインポートする
import Data.Digest.Pure.SHA (hmacSha1, bytestringDigest, showDigest)
import qualified Data.ByteString.Lazy as L
import qualified Data.ByteString.Lazy.Char8 as L8

type ConsumerKey = String
type ConsumerSecret = String
type AccessToken = String
type AccessTokenSecret = String

type URL = String
type Parameter = [(String, String)]

-- signatureを生成する関数
makeSignature :: URL -> RequestMethod -> ConsumerSecret -> AccessTokenSecret -> Parameter -> String
makeSignature url method cSecret aSecret param =
  str6
  where
    -- keyの作成
    key = urlEncode cSecret ++ "&" ++ urlEncode aSecret
    -- keyを秘密鍵として、signatureBaseStringで作成したsignature base stringのHMAC-SHA1を取得する
    str4 :: [Word8]
    str4 = L.unpack $ bytestringDigest $ hmacSha1 (L8.pack key) (L8.pack $ makeSignatureBaseString url method param)
    -- str4をBase64エンコードする
    str5 :: String
    str5 = B64.encode str4
    -- str5をURLエンコードする
    str6 = urlEncode str5

-- signature base stringを生成する関数
makeSignatureBaseString :: URL -> RequestMethod -> Parameter -> String
makeSignatureBaseString url method param =
  str3
  where
    -- メソッド(GET, POST)とURLをエンコードした文字列を"&"で連結する
    str1 = show method ++ "&" ++ urlEncode url
    -- paramのキー1=paramの値1&....&paramのキーn=paramの値n
    -- 【重要】パラメータはソートしておかないといけない
    str2 = intercalate "&" $ map concatPair (sort param)
    -- str2をURLエンコードする
    str2' = urlEncode str2
    -- str1とstr2'を"&"で連結する
    str3 = str1 ++ "&" ++ str2'

    concatPair :: (String, String) -> String
    concatPair (x, y) = x ++ "=" ++ y

次にリクエストを作成するコードを書きます。

-- OAuth module

module OAuth (
  OAuth (..),
  oauthRequest
) where

-- 上で作成したSignatureモジュールのインポート
import  Signature

import Codec.Binary.UTF8.String (encodeString, utf8Encode)
import Network.HTTP 
import Network.URI
import Network.HTTP.Proxy (parseProxy)
import Network.Browser (browse, request, setProxy, request)
import Data.Maybe
import Data.List
import System.Time (ClockTime(..), getClockTime)
import Control.Applicative ((<$>))
import System.Random (randomRIO)

-- OAuthデータ型の定義
data OAuth = OAuth {
     consumerKey :: String,
     consumerSecret :: String,
     accessToken :: String,
     accessTokenSecret :: String
     } deriving (Show, Eq)

-- 乱数の作成
randomInt :: IO Int
randomInt = randomRIO (0, maxBound::Int)

-- システム時間の取得
getUnixTime :: IO Integer
getUnixTime = getUnixTime' <$> getClockTime
  where
    getUnixTime' (TOD i _) = i

-- oauthを使ってリクエストを作成する関数
oauthRequest :: OAuth -> URL -> RequestMethod -> [(String, String)] -> IO Request_String
oauthRequest oauth url method param = do
  -- 乱数の取得
  nonce <- show <$> randomInt
  -- システム時間の取得	     
  unixTime <- show <$> getUnixTime
  let
    -- パラメータをURlエンコードする
    param' = parameterUrlEncode param
    -- 次のパラメータもURLエンコードする
    oauthParam = parameterUrlEncode
                 [("oauth_consumer_key", consumerKey oauth), 
                  ("oauth_nonce", nonce),
                  ("oauth_signature_method", "HMAC-SHA1"),
                  ("oauth_timestamp",  unixTime), 
                  ("oauth_token", accessToken oauth),
                  ("oauth_version", "1.0")]
    -- Signature.makeSignatureを使ってsignatureを作成する		  
    signature = makeSignature url 
                              method
                              (consumerSecret oauth) 
                              (accessTokenSecret oauth) 
                              (param' ++ oauthParam)
    -- URLに付けるパラメータの作成
    urlParam =  sort (("oauth_signature", signature): (param' ++ oauthParam))
    -- URLの作成
    oauthURL = url ++ "?" ++
               intercalate "&" (map concatParam urlParam)

    concatParam :: (String, String) -> String
    concatParam (x, y) = x ++ "=" ++ y

    parameterUrlEncode :: [(String, String)] -> [(String, String)]
    parameterUrlEncode = map $ \(x, y) -> (urlEncode x, urlEncode y)
  -- リクエストの作成
  return $  Request {
             rqURI = fromJust $ parseURI oauthURL,
             rqMethod = method,
             rqHeaders = [],
             rqBody = ""
            }

これでリクエストを作成する関数ができたので、実際に使ってみます。

ここでひとつはまったのは、日本語のツイート。
日本語のツイートには、
Codec.Binary.UTF8.String.encodeString
をつかいます。

-- OAuthTwitter.hs

module OAuthTwitter where

-- 上で作成したモジュールOAuthのインポート
import OAuth
-- 日本語をツイートするためのUTF8モジュールのインポート
import Codec.Binary.UTF8.String (encodeString)
import Network.HTTP 
import Network.HTTP.Proxy (parseProxy)
import Network.Browser (browse, request, setProxy, request)
import Data.Maybe
import Data.List

-- user timelineの取得
userTimeline :: OAuth -> [(String, String)] -> IO Request_String
userTimeline oauth param =  
 oauthRequest oauth userTimelineURL GET param

-- friends timelineの取得
friendsTimeline :: OAuth -> IO Request_String
friendsTimeline oauth = do
  oauthRequest oauth friendsTimelineURL GET []

-- ツイートする
update :: OAuth -> String -> IO Request_String
update oauth status =
  oauthRequest oauth updateURL POST [("status", status)]

-- リストの取得
listTimeline :: OAuth -> String -> String -> IO Request_String
listTimeline oauth userName listName =
  oauthRequest oauth ("http://api.twitter.com/1/" ++ userName ++ "/lists/" ++ listName ++ "/statuses.json") GET []

-- 
-- Define URL and oauth for Twitter
--
requestTokenURL = "http://api.twitter.com/oauth/request_token"
accessTokenURL = "http://api.twitter.com/oauth/access_token"

updateURL = "http://api.twitter.com/1/statuses/update.json"
userTimelineURL = "http://api.twitter.com/1/statuses/user_timeline.json"
friendsTimelineURL = "http://api.twitter.com/1/statuses/friends_timeline.json"

cKey = "****"
cSecret = "****"
aToken = "****"
aSecret = "****"

oauth = OAuth {
          consumerKey = cKey,
          consumerSecret = cSecret,
          accessToken = aToken, 
          accessTokenSecret = aSecret
}

main :: IO ()
main = do
  -- rq <- userTimeline oauth [("id", "screenName")]
  -- rq <- friendsTimeline oauth

  -- 日本語を使うために、encodeStringを使います。
  rq <- update oauth $ encodeString "テストツイート"
  -- rq <- listTimeline oauth "screenName" "listName"
  (uri, res) <- browse $ do
  	 -- プロキシを使うには次をコメントアウトする
         -- setProxy . fromJust $ parseProxy "proxy.server:8080"
         request $ rq
  putStrLn $ rspBody res

参考にしたページは、
twitter developers Authentication】
http://dev.twitter.com/pages/auth
【EAGLE雑記】
http://d.hatena.ne.jp/eagletmt/20100820/1282253083
です。

あと、OAuthを理解、実装するにあたって、次の本が大変参考になりました。

Twitter API プログラミング

Twitter API プログラミング

HaskellでHTTP

HaskellでHTTP!!ということでやってみました。
具体的には、指定先のHTMLを取得してきます。
サンプルコードです。

import Network.HTTP
import Network.HTTP.Proxy (parseProxy)
import Network.Browser (browse, request, setProxy, request)
import Data.Maybe

main :: IO ()
main = do
  -- リクエストを送り、レスポンスを取得する。
  (uri, res) <- browse $ request $ getRequest "http://google.co.jp"
  -- レスポンスの表示
  putStr $ rspBody res

上のプログラムで使っている主な関数の型は

Network.HTTP
  getRequest :: String -> Request_String
Network.HTTP.Base
  type Request_String = Request String
Network.Browser
  request :: HStream ty => Request ty -> BrowserAction (HandleStream ty) (URI, Response ty)
  browse :: BrowserAction conn a -> IO a


プロキシを使う場合には次のようにします。

main :: IO ()
main = do
  (uri, res) <- browse $ do
         -- プロキシを設定してリクエストを送る
         setProxy . fromJust $ parseProxy "proxy.server:8080"
         request $ getRequest "http://google.co.jp"
  putStr $ rspBody res

プロキシ関連の関数の型は次のようになります。

Network.HTTP.Proxy
  parseProxy :: String -> Maybe Proxy
Network.HTTP.Base
  setProxy :: Proxy -> BrowserAction t ()