Atom UI themeを公開しました

seti-black-ui」というAtom UI themeを公開しました。

f:id:blue_9:20180621002648p:plain
seti-black-ui

特徴

・黒いです
・ミニマルなデザインです

atom.io

動機

元々「seti-ui」というthemeを使っていましたが、自分好みに改良を加えているうちに、オリジナルとは異なるものになっていったので、公開することにしました。(seti-uiの作者Jesse Weed氏に謝意を表す)
ただ、あくまでもオレオレテーマなので、自分のためだけに改良して、呑気にリリースする予定です。
syntax themeも近い内に公開する予定です。

興味がある方は是非ご利用ください。

Chrome拡張機能「その本、メルカリで!」を公開しました

Chrome拡張機能その本、メルカリで!」を公開しました。

What is it?

amazonで見ている本が、メルカリで何円なのか分かります。

f:id:blue_9:20180610201721p:plain
amazonページに、メルカリでの値段が表示されます

f:id:blue_9:20180610201725p:plain
クリックするとメルカリの検索ページに飛びます

メリット

amazonでの値段とメルカリでの値段が比較できます(中古本はメルカリの方が安い場合が多い)
・ワンクリックでメルカリの検索ページに飛べます


以下で公開しているので、興味がある方は是非ご利用ください。
chrome.google.com

ダイクストラ法(C言語)

ダイクストラ法について調べてみると、思った以上に解説が少ない。それでもアルゴリズムを理解する分には、充分にある。
しかし、実装については別だ。
ダイクストラ法 C言語」でググって表示されるサイトに載っているコードはまともに動かない。
だから、C言語ダイクストラ法を書くことにした。


アルゴリズムの解説では
http://nw.tsuda.ac.jp/lec/dijkstra/
がとても素晴らしい。大変参考になりました。ありがとうございました。
しかし、このサイトではPythonJavaでの実装のみ。加えて、優先順位付きキューを使用している。C言語には、そんな高級なデータ構造はないので、普通に配列を使って実装します。
以下がそのコード。(コメントは参考サイトのアルゴリズム解説の文を用いました && コメントは変数名は冗長に書きました)

#include <stdio.h>

#define INF 10000000
#define SIZE 1000
#define TRUE 1
#define FALSE 0

int DIST[SIZE][SIZE];
int COST[SIZE];
int VIA[SIZE];
int N;
char USED[SIZE];

int dijkstra(int start, int goal)
{
  int min, target;

  COST[start] = 0;

  while(1){

    /* 未確定の中から距離が最も小さい地点(a)を選んで、その距離を その地点の最小距離として確定します */
    min = INF;
    for(int i = 0; i < N; i++){
      if(!USED[i] && min > COST[i]) {
        min = COST[i];
        target = i;
      }
    }

    /* 全ての地点の最短経路が確定 */
    if(target == goal)
      return COST[goal];

    /* 今確定した場所から「直接つながっている」かつ「未確定の」地点に関して、
    今確定した場所を経由した場合の距離を計算し、今までの距離よりも小さければ書き直します。 */
    for(int neighboring = 0; neighboring < N; neighboring++){
      if(COST[neighboring] > DIST[target][neighboring] + COST[target]) {
        COST[neighboring] = DIST[target][neighboring] + COST[target];
        VIA[neighboring] = target;
      }
    }
    USED[target] = TRUE;
  }
}

int main(void){
  int r;
  int a,b,l;
  int s,d;

  /* 初期化 */
  for(int i = 0; i < SIZE; i++) {
    COST[i] = INF;
    USED[i] = FALSE;
    VIA[i] = -1;
    for(int j = 0; j < SIZE; j++)
      DIST[i][j] = INF;
  }

  /* 入力 */
  scanf("%d %d", &N, &r);
  for(int i = 0; i < r; i++){
    scanf("%d %d %d", &a, &b, &l);
    DIST[a][b] = l;
  }
  scanf("%d %d", &s, &d);

  /* ダイクストラ法で最短経路を求める */
  printf("distance:%d\n", dijkstra(s,d));

  /* 経路を表示(ゴールから) */
  int node = d;
  printf("%d", node);
  while(1){
    node = VIA[node];
    printf(" -> %d", node);
    if (node == s) break;
  }

  return 0;
}

実行結果。

% ./dijkstra
7 10
0 1 30
0 3 10
0 2 15
1 3 25
1 4 60
2 3 40
2 5 20
3 6 35
4 6 20
5 6 30
0 6
distance:45
6 -> 3 -> 0

データは参考サイトの項「課題2」にあります。

2017を振り返る

年末バタバタで今年を振り返るのは無理そうだと思ったが、今年も残り2時間というところで、急に手持ち無沙汰になったので簡単に振り返る。

1月~3月

CTF4bに行ったことをきっかけにCTFを始めた。Pwnを中心にやっていたこともあり、低レイヤの魅力にハマって、色々な文献に目を通して、世界が広がった。勉強会にも参加して、人脈を作れたことも良かった。
大学卒業した。みんな4年間ありがとう!

5月

逆求人イベントに行って、色々な会社と接することができた。そこで、やっぱりエンジニアではなく、シンクタンク・コンサル行こうと思い、シンクタンク・コンサルのインターンを探し始めた。
SecHackが合格になった。技術やるならセキュリティがいいなと思っていたので、嬉しかった。受かったからには、頑張らないとなという思いから、苦手なネットワークの勉強をした。(結局、一年取り組むことになる)

6月、7月

インターン応募のためにES書きまくって面接しまくった。コンサルのための本とかたくさん読んで、ESの書き方、面接での答え方を勉強して、実践、反省を繰り返した。最初の頃は下手くそだったけど、途中からは受かりまくったので、やり方は間違ってなかったらしい。この辺から秋までは技術の勉強は対してやらなかったので、エンジニアとしての成長はあまりない。

8月

旅に行けた。絶景最高。

9月

2社インターン。すごく優秀な社員さんと学生に囲まれて幸せだった。結果はボロボロだったけど、コンサルの思考・仕事術を少しは盗めたことは大きい。仮説思考・アウトプットドリンに触れることができて良かった。

10月

北海道寒かったな。
インターンのためにまたESを書き始めた。

11月

祖父が亡くなった。色々考えた。

12月

インターン。相変わらずアウトプットドリブン。金融業界に興味を持ち始めた。

総まとめ

コツコツとネットワークの勉強を続けて、だいぶ理解が深まった。一番苦手な分野が苦手でなくなったことは大きい。
面接をたくさんこなして、面接が得意になった。
コンサルの仕事が出来て良かった。コンサルの仕事術はどの仕事をしても役に立つ。

6月から忙しくなって、4つくらいプロジェクトは回すわ、各地に飛ばなくてはならないわで、途中体調も崩したし、疲れた。しかし、精神的充実は大きい。それらのプロジェクトは今も続いてるわけだけど、全体的にパフォーマンスが悪い。一つ一つの完成度は8割くらいでイマイチだと思ってる。来年はマルチタスクをこなせるようになりたい。(そのためには何をすべきなのか?)

来年やりたいこと

「書く」ことに時間を割きたい。
47都道府県制覇したい。(現在40都道府県)
2月から圧倒的当事者意識の会社で働くので、気を引き締めて頑張りたい。


あっ、音楽では渋谷系にハマった。
オザケンスカパラピチカート・ファイヴ。一年ずっと聞いてたな。あと、The Strokes

皆様一年ありがとうございました!
来年も是非よろしくお願いいたします!!

ELFとELFローダについて

ROPというハッキング手法がある。これを勉強していたら、共有ライブラリがメモリ上のどこに配置されるのか、それはどのタイミングなのか気になり始めた。そして、色々と文献を漁ってぼんやりとしたことは分かってきたが、知識が定着したとは言えないので、自分の理解をより深めるため、知識を定着させるため以下に、ELF周りの話を記す。

$uname -a
Linux VM 3.13.0-24-generic #47-Ubuntu SMP Fri May 2 23:30:00 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux

$lsb_release -a
No LSB modules are available.
Distributor ID:	Ubuntu
Description:	Ubuntu 14.04.5 LTS
Release:	14.04
Codename:	trusty

$ gcc --version
gcc (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4

// ASLR無効化
$ sudo sysctl -w kernel.randomize_va_space=0
kernel.randomize_va_space = 0

hello.c

#include <stdio.h>

int main(void)
{
    printf("Hello, World!\n");
    return 0;
}

ELF(Executable and Linking Format)

ELFとは、実行可能バイナリやオブジェクトファイルなどのフォーマットを規定したもの。ELFフォーマットのファイルは、ELFヘッダが先頭にあり、プログラムヘッダテーブル及びセクションテーブルがその後にあります。これらのヘッダ構造はelf.hに記述されている。

// http://lxr.free-electrons.com/source/tools/objtool/elf.h#L69
struct elf {
              Elf *elf;
              GElf_Ehdr ehdr;
              int fd;
              char *name;
              struct list_head sections;              
              DECLARE_HASHTABLE(rela_hash, 16);
      };

ELFヘッダ

ELFヘッダはELFファイルの先頭に必ず存在し、そのファイルがELFファイルであることを示す。

ELFヘッダはelf->ehdrのことで以下のように定義されている。

// http://lxr.free-electrons.com/source/include/uapi/linux/elf.h#L235

typedef struct elf64_hdr {
         unsigned char e_ident[EI_NIDENT];     /* ELF "magic number" */
         Elf64_Half e_type;
         Elf64_Half e_machine;
         Elf64_Word e_version;
         Elf64_Addr e_entry;           /* Entry point virtual address */
         Elf64_Off e_phoff;            /* Program header table file offset */
         Elf64_Off e_shoff;            /* Section header table file offset */
         Elf64_Word e_flags;
         Elf64_Half e_ehsize;
         Elf64_Half e_phentsize;
         Elf64_Half e_phnum;
         Elf64_Half e_shentsize;
         Elf64_Half e_shnum;
         Elf64_Half e_shstrndx;
         } Elf64_Ehdr;
$ readelf -h a.out 
ELF ヘッダ:
  マジック:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  クラス:                            ELF64
  データ:                            2 の補数、リトルエンディアン
  バージョン:                        1 (current)
  OS/ABI:                            UNIX - System V
  ABI バージョン:                    0
  型:                                EXEC (実行可能ファイル)
  マシン:                            Advanced Micro Devices X86-64
  バージョン:                        0x1
  エントリポイントアドレス:               0x400440
  プログラムの開始ヘッダ:          64 (バイト)
  セクションヘッダ始点:          4472 (バイト)
  フラグ:                            0x0
  このヘッダのサイズ:                64 (バイト)
  プログラムヘッダサイズ:            56 (バイト)
  プログラムヘッダ数:                9
  セクションヘッダ:                  64 (バイト)
  セクションヘッダサイズ:            30
  セクションヘッダ文字列表索引:      27

プログラムヘッダ

プログラムヘッダテーブルはELFヘッダのe_phoffで指定されるオフセットから始まり、e_phentsizeとe_phnumで決まる大きさのテーブルからなります。e_phentsizeがテーブルの中のプログラムヘッダのサイズを表し、e_phnumがそのテーブルの中にいくつセッションヘッダがあるか示しています。

$ readelf -l a.out 

Elf ファイルタイプは EXEC (実行可能ファイル) です
エントリポイント 0x400440
9 個のプログラムヘッダ、始点オフセット 64

プログラムヘッダ:
  タイプ        オフセット          仮想Addr           物理Addr
                 ファイルサイズ        メモリサイズ         フラグ 整列
  PHDR           0x0000000000000040 0x0000000000400040 0x0000000000400040
                 0x00000000000001f8 0x00000000000001f8  R E    8
  INTERP         0x0000000000000238 0x0000000000400238 0x0000000000400238
                 0x000000000000001c 0x000000000000001c  R      1
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000
                 0x000000000000070c 0x000000000000070c  R E    200000
  LOAD           0x0000000000000e10 0x0000000000600e10 0x0000000000600e10
                 0x0000000000000230 0x0000000000000238  RW     200000
  DYNAMIC        0x0000000000000e28 0x0000000000600e28 0x0000000000600e28
                 0x00000000000001d0 0x00000000000001d0  RW     8
  NOTE           0x0000000000000254 0x0000000000400254 0x0000000000400254
                 0x0000000000000044 0x0000000000000044  R      4
  GNU_EH_FRAME   0x00000000000005e4 0x00000000004005e4 0x00000000004005e4
                 0x0000000000000034 0x0000000000000034  R      4
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     10
  GNU_RELRO      0x0000000000000e10 0x0000000000600e10 0x0000000000600e10
                 0x00000000000001f0 0x00000000000001f0  R      1


 セグメントマッピングへのセクション:
  セグメントセクション...
   00     
   01     .interp 
   02     .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .text .fini .rodata .eh_frame_hdr .eh_frame 
   03     .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss 
   04     .dynamic 
   05     .note.ABI-tag .note.gnu.build-id 
   06     .eh_frame_hdr 
   07     
   08     .init_array .fini_array .jcr .dynamic .got 

ここでなぜ0x0000000000400000にELFファイルが配置されているか知りたい場合は
ELF実行ファイルのメモリ配置はどのように決まるのか - ももいろテクノロジー
を参照すると良い。
INTERPとは動的リンクを実際に処理するインタプリタ(リンカ)のことである。
http://lxr.free-electrons.com/source/fs/binfmt_elf.c?v=3.2#L559にELFファイルをメモリにロードする部分がある(多分)。
そこを読み解く前に、linux_binprm 型という、構造体の説明。これは、ユーザー空間にある引数をカーネル側で保持・加工する為のデータ構造らしい。

// http://lxr.free-electrons.com/source/include/linux/binfmts.h?v=3.2#L28

/*  
  * This structure is used to hold the arguments that are used when loading binaries.
  */
  struct linux_binprm {
          char buf[BINPRM_BUF_SIZE];
  #ifdef CONFIG_MMU
          struct vm_area_struct *vma;
          unsigned long vma_pages;
  #else
  # define MAX_ARG_PAGES  32
          struct page *page[MAX_ARG_PAGES];
  #endif
          struct mm_struct *mm;
          unsigned long p; /* current top of mem */
          unsigned int
                  cred_prepared:1,/* true if creds already prepared (multiple
                                   * preps happen for interpreters) */
                  cap_effective:1;/* true if has elevated effective capabilities,
                                   * false if not; except for init which inherits
                                   * its parent's caps anyway */
  #ifdef __alpha__
          unsigned int taso:1;
  #endif
          unsigned int recursion_depth;
          struct file * file;
          struct cred *cred;      /* new credentials * /
          int unsafe;             /* how unsafe this exec is (mask of LSM_UNSAFE_*) */
          unsigned int per_clear; /* bits to clear in current->personality */
          int argc, envc;
          const char * filename;  /* Name of binary as seen by procps */
          const char * interp;    /* Name of the binary really executed. Most
                                     of the time same as filename, but could be
                                     different for binfmt_{misc,script} */
          unsigned interp_flags;
          unsigned interp_data;
          unsigned long loader, exec;
  };

はじめに、ELFヘッダのサイズ分をmallocして、ELFヘッダを読み込み、各要素をチェックする。次に、全てのプログラムヘッダのサイズ分をmallocして、全てプログラムヘッダを読み込む。そして、それを順にチェックしていき、p_type(セグメンタイプ)がINTERPの場合、その内容を読み取る。つまり、elf_interpreter="/lib64/ld-linux-x86-64.so.2"。ここでは、open_exec()した後、interpreterのBINPRM_BUF_SIZE分(128byte)をloc->interp_elf_exに代入。つまりはinterpreterの情報。

次にp_typeがPT_GNU_STACKセクションの場合、ELFヘッダのp_flagsをチェックしてスタック上でのコード実行の可否を決める。(ここはDEPと関わってくる)。
次に、INTERPの妥当性(マジックナンバーと、アーキテクチャの適合性)をチェック。

flush_old_exec()で現在のprogramの情報を消し、新しいprogramのための情報にcurrentの情報をいれかえていく。current は、カーネル中で現在実行中のプロセスの task_struct 構造体 を保持する変数。(プロセス、リンク、task_struct構造体)
SET_PERSONALITY()で親のpersonalityを継承する。その後、currentの情報を更新していく。(ここはlinuxのプロセス管理のお話なので、いつか勉強したい)

次はELFをmmapingする。p_typeがPT_LOADの場合にp_flagsを調べ、パーミッションを設定する。ヒープセクションがbssセクションより下位アドレスにあった場合、対象領域をクリアする?
その後、 MAP_PRIVATEとe_typeがET_EXECであった場合ET_MAP_FIXEDをelf_flagsに設定。(https://linuxjm.osdn.jp/html/LDP_man-pages/man2/mmap.2.html/)
共有オブジェクトならランダムフラグを見てアドレス計算。
オフセットとか計算したら、elf_map(おそらく中身はmmap()、セマフォとかしてるし、後々解析する予定)する。
mmap()した先のアドレス、セクションのサイズ等をチェックする。
その後、ヒープセクション、bssセクションの調整?(よくわかっていない)

load_elf_interp()でinterpreterを読み込む。読み込もうとしているinterpreterのe_typeがET_EXECかET_DYN(共有ライブラリ)なのか、アーキテクチャは合っているか、interpreterのfile operation等をチェック。e_phentsizeとe_phnumでサイズチェック。
interpreterのサイズ分のメモリを確保して、kernel_read()する。その後、elf_map()で共有ライブラリをmappingする。
その後、読み込んだアドレス等をチェック。そして、current情報を更新。

453                         map_addr = elf_map(interpreter, load_addr + vaddr,
454                                         eppnt, elf_prot, elf_type, total_size);

462                         if (!load_addr_set &&
463                             interp_elf_ex->e_type == ET_DYN) {
464                                 load_addr = map_addr - ELF_PAGESTART(vaddr);
465                                 load_addr_set = 1;
466                         }

523         error = load_addr;

528         return error;

そして、start_thread(regs, elf_entry, bprm->p)で先ほどのerrorがeipに設定される。
この呼び出し元に戻ると、どうやらeipから実行するらしく、すなわち共有ライブラリが実行されることになる。

感想

OSのメモリマッピングの知識が欠けていたので、mmap()周辺が正しく解析できていない。よって、共有ライブラリの読み込み先アドレス等の計算が未だに分かっていないため、目的は達成できていない(参考文献読んだら理解できた)。メモリマッピングの勉強をしたら、また読んでみよう。
linux kernelのソースコードを初めて読んだ。面白くて仕方なかった。しかし、特有のデータ構造や頻繁に使われる関数等を一々追っていたため、記事の内容もまとまらず、知識も断片的なものとなった。これから他のところを読んだ時に、今回の知識は役に立つのであろうか?
もしそうでない場合は、また色々と追っているうちに闇に吸い込まれていくわけであるが、いつかは塵も積もり山となるであろう。

2016年を振り返る

今年は「Hard Work」をテーマとして掲げていた。それは概ね達成できたと思う。
そしてその副産物として、色々得ることができた。
また、初めて経験することが多く、これからの人生で過去を振り返る時にもターミングポイントになるだろうものがあった。
そこで、今年の振り返りと題して、それらを少々まとめることにする。(だいたい時系列)

Interconnect

ラスベガスで行われたIBM Interconnectに参加した。初めての海外だったので、それだけでも充分に目新しいことが多かった。しかし、このイベントではそれ以上に得るものが多かった。
国際的なイベントの規模の大きさ!新しい技術!自分の知らない文化!
刺激が多かった。吸収仕切れない。
あの時の興奮は、今でも頭の中に強く残っている。
IBM InterConnect2016に参加してきました - blue9's 外壁


表彰式

これまで表彰式と無縁の人生を送ってきたため、たいそう嬉しいことだった。
ランチに食べた、いいホテルの高いコース料理は、庶民の舌には美味しいのかよく分からなかった。
1日に3件も取材を受けるなんて、一生に一回あるかないか。いい経験になった。
WIREDは好きなメディアなので、取材されたのは嬉しかったな。
千葉裕也 | メインフレームは「世界への扉を開く鍵」その存在を知って、すべてが始まった « INNOVATION INSIGHTS

北陸旅

金沢は素晴らしい。文化が洗礼されていた。
新潟も良かった。
夏に旅に出られなかったため、無理してでも行って良かった。
f:id:blue_9:20161230223150j:plain
f:id:blue_9:20161230223251j:plain

世界大会

日本代表として、2016 IBM Master the Mainframe World Chimpionshipに参加した。
偶然にも大学の授業が休講だったため、2週間ほど誰にも会わず、じっくり取り組んだ。
問題を与えられたら解きたくなる性分だし、分からないことに頭を悩ますのが大好きなので、幸せな2週間だった。
メインフレーム・コンテストで、正しく理解できてなかったなとか振り返る機会にもなったので良かった。
f:id:blue_9:20161230223826p:plain

インターン

K社とF社でインターンをしてきた。
プログラマをしていたけれど、経営者の方々と近いところで仕事をしていたため、そこから影響を強く受けた。
K社社長のNさん、F社PEのSさんと尊敬すべき方に出会えたことは良かった。
インターンの経験は素晴らしく、自分の進路選択に強い影響を与えた。

ミニマリズム

10月から生活にミニマリズムを取り入れた。
前から気になってはいたけれど、本格的に取り入れるに踏み切ったのは、重大な判断をする機会が増えたからである。

試練は年齢と共に高まる
ゲーテ

重大な判断に迫られる中で、一つ一つの小さな判断が邪魔に思えてきた。
人生の分岐点で右に行くか左に行くか判断しなければならない時に、「今日はどの服を着ようか」なんて判断はどうでもいい。
完全に(そもそも完全とは?)ミニマリズムを取り入れられたわけではないので、これからも試行錯誤する必要がある。
現在のところ、良い感じである。

ヘッセ

夏以降、気分が上がらず、会う人会う人に「元気ないですね」と言われ続けた。
気分が上がらなかっただけで、凹んでいたわけではない。ただ、色々考えていたのである。
そんな時、かつて恩師に勧められたヘルマン・ヘッセの「シッダールタ」を読んだ。
ヘッセには昔から興味はあったが、ここまで共感・感服したのは、この本が一番だ。
それから1920年代のヘッセのエッセイ、詩を読み漁った。
彼は素晴らしい思想を持っていた。僕は傾倒してしまった。
しかし、彼は
「どんな本のどんな1ページも、君に智慧は与えることはできない」
と言う。
シッダルタ (岩波文庫)

2017

人間の記憶は当今の出来事、思想を色濃く反映してしまう。
よって、2016年を現在振り返るのと、未来に振り返るのでは、異なった見え方になるのであろう。
本当はここに私情をもっと記しておきたいが、....やめておこう。

12月に同い年のとても面白い奴に出会った。
「新しい知識を得るためにはリスクが必要だよ」
彼は言った。
ハッとさせられた。
これまで、リスクがないように選択し、重大なことから逃げてきたことを。
そして、選択においてリスクを取る必要が迫っていることに気づきながら、無視していたことを。
その場で、来年のテーマは「リスクを犯せ」にしようと決めた。

昨年は「動」、今年は「Hard Work」、来年は「リスクを犯せ」。

来年も面白い一年になるに違いない。

プログラマとデザイナ

親友

親友に美大でデザインを専攻している者がいる。彼とは古くから親しく、この世界で唯一、自分が考えていること全て話せる存在だ。
少し前に彼から興味深いことを言われた。「君は漫画もカードもゲームも好きではないし、全く趣味は合わない。でも、人生に対する考えが好きだから一緒に遊ぶのだと思う。」
全くその通りである。そんなことは僕もずっと思っていた。でも、一つだけ疑問があった。
彼はデザイナであり、僕はプログラマである。彼は絵が上手で、僕は絵が下手だ。彼はクリエィティブだし、僕はクリエィティブではない。
趣味も違うし、よりパーソナルに近い部分でも違う。しかし、彼と馬が合うのなら、何か共通することがあるのではないか。

デザイナ

最近、様々な業種の方々と仕事をすることがあった。初対面の席でお互い、自分の職業と仕事を選んだ理由、仕事で何を得たいのか自己紹介した。
そこで、二人ほどとても興味深いことを話す方がいた。思考手順が面白かったのだ。彼女らはデザイナだった。その時は職業というより、二人の個人としての魅力が興味を誘っているのだと思った。
そういえば、二人はクールな腕時計を身に着けていた。

消費

その後、デザインを学んでいる友人と遊ぶ機会があった。彼とは高校の時からの関係で、卒業後も定期的に会う仲であるが、基本的には話が合わない。
遊んでいる途中、彼が「この時計買った」と左手首に巻いた腕時計を見せてくれた。Paul Smithのロゴが入っていた。聞くところによると、3万5千円ほどらしい。
「でも、他にも欲しい時計があって、イッセイ・ミヤケのやつなんだ。でも、4万以上するんだよね」彼は楽しそうに話していた。そして、他にも欲しい物がたくさんあるらしい。
別にブランドを身に着けるのは悪いとは思っていない。しかし、次から次へと物欲に燃えている彼は消費そのものであった。それを見て、直感的に彼はデザイナではないなと思った。

アート

僕はその仕事でプログラムを書いていた。でも、これまでとは違う。良い設計を考えて、エレガントなコードを書くことは求められていなかったのだ。求められていたことは、ただただ設計書通りに狂いなくコードを書くこと。
そこに創造はいらなかったのだ。それは退屈であったし、ショックであった。しかし、現実の仕事とはそういうものらしい*1。そこで僕は、自分がプログラムを創造することに愉しみを見出していたことに気づいた。
僕にとって、プログラミングは第一級のアートだったのだ。

創造

件の仕事の最後に、まとめのミーティングがあった。そこでもやはり興味深いことを話す方々がいた。彼らはデザイナであった。
退社後、彼らと飲みに行った。その席で「いい時計してるね」と言われた。僕がつけていたのは、スウェーデンのメーカの1万円くらいの腕時計だった。
声をかけてくれた彼女は白と黒のスタイリッシュな素敵な腕時計をしていたので、「それもいいね」と僕は返した。
「でしょ?これ2500円の20%割引で買えたの!」彼女は答えた。隣のデザイナが光沢のある青の文字盤に金色の時刻点がある美しい時計をしていたのも気になっていたので、「それは?」と聞いてみた。
「これはね1200円だったの」彼女は答えた。安価な物であったが、素晴らしいデザインで、彼女らにとてもお似合いだった。そして個性が光っていた。
そこで、自分に似合う腕時計を身に着けることは創造だと気づいた。

共通点

僕は絵を描いたり、何かをデザインしたいと考えたことはない。非クリエィティブな人間だ。だから、デザイナという職業とはかけ離れていると思っていた。
しかし、振り返ってみると、僕がプログラミングにこれほど熱狂できるのは、「どうすれば良い設計になるだろう?どう書けば、もっとエレガントになるのだろう?」と考え続けることができたからであると思う。
そこにhackしがいがあったのだ。創造があったのだ。
これで彼と共通することが分かった。対象は違っていても、お互い何かを創造することを楽しむ心を持っていたのだ。
そして、創造するための思考手順に似たものをお互い感じ取っていたのだ。

いつか彼にこのことを話してみようと思う。きっと、創造に富んだ返答をしてくれるだろう。

*1:もちろん会社によって違う。僕がいたところはウォーターフォールモデルで開発が進められていた。設計書に逆らってはいけないのだ