C言語でMySQLにinsertする書き方

sql_strの箇所をselectからinsert文に変えるだけです。update, deleteも一緒です。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mysql/mysql.h>

int main(){
	MYSQL *conn = NULL;
	MYSQL_RES *resp = NULL;
     // MYSQL_ROW row;
	char sql_str[255];
	char *sql_serv = "localhost";
	char *user = "root";
	char *passwd = "hogehoge";
	char *db_name = "test";

	memset( &sql_str[0], 0x00, sizeof(sql_str));

	conn = mysql_init(NULL);
	if(!mysql_real_connect(conn, sql_serv, user, passwd, db_name, 0, NULL, 0)){ 
		 exit(-1);
	}

	snprintf(&sql_str[0], sizeof(sql_str)-1, "insert into test (id,name) values (4, 'ddd')"); 
	if(mysql_query(conn, &sql_str[0])){ 
		mysql_close(conn);
		exit(-1);
	}

	mysql_free_result(resp);
	mysql_close(conn);

	return 0;
}

### コンパイル&実行
$ gcc -Wall -o dev hello.c -lmysqlclient -L/usr/lib/x86_64-linux-gnu/
$ ./dev

### 動作確認
mysql> select * from test;
+——+——+
| id | name |
+——+——+
| 1 | aaa |
| 2 | bbb |
| 3 | ccc |
| 4 | ddd |
+——+——+
4 rows in set (0.00 sec)

なるほど、DBとの接続はわかった。次はGUIか。

C言語でMySQLのレコードからSelectする方法

### ソースコード

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mysql/mysql.h>

int main(){
	MYSQL *conn = NULL;
	MYSQL_RES *resp = NULL;
	MYSQL_ROW row;
	char sql_str[255];
	char *sql_serv = "localhost";
	char *user = "root";
	char *passwd = "hogehoge";
	char *db_name = "test";

	memset( &sql_str[0], 0x00, sizeof(sql_str)); // memsetはメモリに指定バイト数分の値をセット, 0x00はヌル文字

	conn = mysql_init(NULL);
	if(!mysql_real_connect(conn, sql_serv, user, passwd, db_name, 0, NULL, 0)){ // サーバ接続開始 mysql_real_connect(mysql, host, user,passwd,db, port,  unix_socket, client_flag)
		 exit(-1);
	}

	// クエリ実行
	snprintf(&sql_str[0], sizeof(sql_str)-1, "select * from test");  // snprintfは指定文字数分だけ文字配列に書き込む
	if(mysql_query(conn, &sql_str[0])){  // sql_str によって指示される SQL ステートメントを実行
		mysql_close(conn);
		exit(-1);
	}

	// レスポンス
	resp = mysql_use_result(conn);  // 結果セットの取得を開始
	while((row = mysql_fetch_row(resp)) != NULL){   // 結果を添字配列として取得する
		printf("%d: %s\n", atoi(row[0]), row[1]);
	}

	mysql_free_result(resp);  // 結果保持用メモリを開放
	mysql_close(conn); // MySQL 接続を閉じる

	return 0;
}

### ライブラリのlibmysqlclient.soの配置場所を確認
$ find / -name “libmysqlclient.so”
/usr/lib/x86_64-linux-gnu/libmysqlclient.so

### コンパイル、実行
// -Wは警告メッセージ抑制, -Lはオブジェクトライブラリ
$ gcc -Wall -o dev hello.c -lmysqlclient -L/usr/lib/x86_64-linux-gnu/
$ ./dev
1: aaa
2: bbb
3: ccc

おいおい、マジかよ。。。
感動した。

C言語でmysqlを使う前準備

### MySQLにデータ挿入
$ mysql -u root -p
> use test;
> create table test(id int,name varchar(256));
> insert into test(id,name) values(1,”aaa”);
> insert into test(id,name) values(2,”bbb”);
> insert into test(id,name) values(3,”ccc”);
> select * from test;

### ubuntu18.04にmysql/mysql.hが入っているか確認
hello.c

#include <stdio.h>
#include <mysql/mysql.h>


int main(){

	return 0;
}

$ gcc -o dev hello.c
hello.c:2:10: fatal error: mysql/mysql.h: No such file or directory
#include
^~~~~~~~~~~~~~~
compilation terminated.
-> 入っていない

### libmysqlclient-devをインストール
$ sudo apt-get -y install libmysqlclient-dev
$ gcc -o dev hello.c
-> 無事コンパイル

trustyのlibmysqlclient-dev.installのgithub見たが、includeしてるって事か?

usr/include/mysql/*.h
usr/include/mysql/mysql/*.h
usr/include/mysql/mysql/*.h.pp
usr/include/mysql/mysql/psi/*.h
usr/lib/*/libmysqlclient.a
usr/lib/*/libmysqlclient.so
usr/lib/*/libmysqlservices.a
usr/bin/mysql_config
usr/bin/mysql_config_editor
usr/share/man/man1/mysql_config.1
usr/share/man/man1/mysql_config_editor.1
# legal
usr/share/doc/libmysqlclient-dev/COPYING
usr/share/doc/libmysqlclient-dev/README

.aファイルはStaticライブラリの事らしい。
あまり見ないな。

Ubuntu18.04 mysql5.7でAccess denied for user ‘root’@’localhost’

ubuntu18.04でtasksel install lamp-serverでmysqlを入れ、パスワードの設定をしたが、アクセスできない時

$ sudo mysql -u root
mysql> update user set authentication_string=password(“password”) where User=’root’;
$ mysql -u root -p
Enter password:
ERROR 1698 (28000): Access denied for user ‘root’@’localhost’

むむむむ?

MySQL 5.7では認証にauth_socket plugin(MySQL :: Security in MySQL :: 6.1.11 Socket Peer-Credential Pluggable Authentication) というものを使っていて、これを使っているとパスワードではアクセスできず、MySQLのユーザー名と、システムのユーザー名が一致しているときのみアクセスできる

mysql> SELECT User, Host, plugin FROM mysql.user;
+——————+———–+———————–+
| User | Host | plugin |
+——————+———–+———————–+
| root | localhost | auth_socket |
| mysql.session | localhost | mysql_native_password |
| mysql.sys | localhost | mysql_native_password |
| debian-sys-maint | localhost | mysql_native_password |
+——————+———–+———————–+
4 rows in set (0.00 sec)

mysql> UPDATE user SET plugin=’mysql_native_password’ WHERE User=’root’;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1 Changed: 1 Warnings: 0

mysql> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.00 sec)

$ mysql -u root -p

C言語でfreeコマンドを書きたい

メモリを調べるにはfree, top, ps, vmstat, /proc/meminfoなどがある

$ free
total used free shared buff/cache available
Mem: 1008620 312640 186604 2200 509376 542844
Swap: 0 0 0

– 単位はバイト
– メモリ8Gのマシンなら、8G

■freeコマンドの項目
total: 全メモリ
free: 全く利用されていないメモリ
buff/cache: キャッシュとして利用されているけど、解放できる可能性のあるメモリ
used: = total – free – buff/cache

■swapとは
メモリ-の一部をディスク(HDDなど)に退避させて計算を続ける事ができる。この様にしてメモリ-の容量を実際よりも大きく見せるメカニズムを仮想メモリという。そのためにディスクにメモリを退避させるための領域をswapという

メモリ消費量を見積もるには /proc/pid/status の VmHWM

#include <stdio.h>


int main(){

	char command[128];

	sprintf(command, "grep VmHWM /proc/%d/status", getpid()); // getpid():プロセスのIDを知る
	system(command);

	return 0;
}

$ ./dev
VmHWM: 720 kB

■物理メモリ使用量の調べ方
$ ps
PID TTY TIME CMD
459 pts/0 00:00:00 bash
687 pts/0 00:00:00 bash
2490 pts/0 00:00:00 bash
4458 pts/0 00:00:00 ps
18043 pts/0 00:00:00 dev
18140 pts/0 00:00:00 dev
18499 pts/0 00:00:00 dev
18505 pts/0 00:00:00 dev
32396 pts/0 00:00:00 bash
32437 pts/0 00:00:00 bash
32556 pts/0 00:00:00 bash
32600 pts/0 00:00:00 bash

$ cat /proc/18043/status
Name: dev
Umask: 0002
State: T (stopped)
Tgid: 18043
Ngid: 0
Pid: 18043
PPid: 2490
TracerPid: 0
Uid: 1000 1000 1000 1000
Gid: 1000 1000 1000 1000
FDSize: 256
Groups: 1000
NStgid: 18043
NSpid: 18043
NSpgid: 18043
NSsid: 2490
VmPeak: 4508 kB
VmSize: 4508 kB
VmLck: 0 kB
VmPin: 0 kB
VmHWM: 704 kB
VmRSS: 700 kB
RssAnon: 60 kB
RssFile: 640 kB
RssShmem: 0 kB
VmData: 176 kB
VmStk: 132 kB
VmExe: 4 kB
VmLib: 2112 kB
VmPTE: 56 kB
VmSwap: 0 kB
HugetlbPages: 0 kB
CoreDumping: 0
Threads: 1
SigQ: 0/3838
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000000000
SigIgn: 0000000000000000
SigCgt: 0000000000000000
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: 0000003fffffffff
CapAmb: 0000000000000000
NoNewPrivs: 0
Seccomp: 0
Speculation_Store_Bypass: vulnerable
Cpus_allowed: 3
Cpus_allowed_list: 0-1
Mems_allowed: 00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000001
Mems_allowed_list: 0
voluntary_ctxt_switches: 3
nonvoluntary_ctxt_switches: 2

less, more, cat, tailの違い

cat
L 標準出力

more
L ファイル末尾まで行くと終了

less
L ファイル内容を閲覧、 qで終了

tail
L ファイル末尾だけ表示

実際にコマンドを打って確認しましょう。
$ cat hello.c
$ more hello.c
$ less hello.c

lessだと次の操作に移るのに[q]を打たないといけないので、catの方を多用していますが、行が長いファイルをじっくり確認したい場合はlessの方が使いやすい、と言ってる方もいます。

C言語でaproposコマンドを書きたい

■要求定義
– manページの内容を検索する
– whatisと名付けられたいくつかのファイルを検索している

e.g.
$ apropos passwd
chgpasswd (8) – update group passwords in batch mode
chpasswd (8) – update passwords in batch mode
fgetpwent_r (3) – get passwd file entry reentrantly
getpwent_r (3) – get passwd file entry reentrantly
gpasswd (1) – administer /etc/group and /etc/gshadow
grub-mkpasswd-pbkdf2 (1) – generate hashed password for GRUB
htpasswd (1) – Manage user files for basic authentication
openssl-passwd (1ssl) – compute password hashes
pam_localuser (8) – require users to be listed in /etc/passwd
passwd (1) – change user password
passwd (1ssl) – compute password hashes
passwd (5) – the password file
passwd2des (3) – RFS password encryption
update-passwd (8) – safely update /etc/passwd, /etc/shadow and /etc/group

キーワード検索の原理はregexと同じか。。

C言語でmanコマンドを書きたい

■要求定義
– コマンドのマニュアルを表示する

$ man 調べる語句

/usr/share/man 直下にソースコードがある
man1 ユーザプログラム
man2 システム呼び出し
man3 ライブラリ呼び出し
man4 特殊ファイル
man5 ファイルフォーマット
man6 ゲーム
man7 その他
man8 システム管理者

e.g.
/usr/share/man/man1/ln.1.gz を解凍する

.\" DO NOT MODIFY THIS FILE!  It was generated by help2man 1.47.3.
.TH LN "1" "January 2018" "GNU coreutils 8.28" "User Commands"
.SH NAME
ln \- make links between files
.SH SYNOPSIS
.B ln
[\fI\,OPTION\/\fR]... [\fI\,-T\/\fR] \fI\,TARGET LINK_NAME   (1st form)\/\fR
.br
.B ln
[\fI\,OPTION\/\fR]... \fI\,TARGET                  (2nd form)\/\fR
.br
.B ln
[\fI\,OPTION\/\fR]... \fI\,TARGET\/\fR... \fI\,DIRECTORY     (3rd form)\/\fR
.br
.B ln
[\fI\,OPTION\/\fR]... \fI\,-t DIRECTORY TARGET\/\fR...  \fI\,(4th form)\/\fR
.SH DESCRIPTION
.\" Add any additional description here
.PP
In the 1st form, create a link to TARGET with the name LINK_NAME.
// 省略

$ man ln
make links between files と書かれているのがわかります。

■検討
– gzファイルを配置して、解凍して中身を読み取るプログラムを書いていく
$ ls -l
total 20
-rwxrwxr-x 1 vagrant vagrant 8344 May 18 08:34 dev
-rw-rw-r– 1 vagrant vagrant 267 May 18 08:34 hello.c
-rw-rw-r– 1 vagrant vagrant 1728 May 18 10:53 ln.1.gz

#include <stdio.h>
#include <stdlib.h>

int main(){

	char cmd[256];
	char str[256];
	char filename[] = "./ln.1.gz"; //  /usr/share/man/man1/ln.1.gz

	sprintf(cmd, "gzip -d %s", filename); // gzip -d(ファイル展開)が指す書式文字列に従って cmdが指す文字配列へ書き込み
	
	FILE *fp;
	fp = popen(cmd, "r"); // popenはプロセスをオープンする
	while(fgets(str, 256, fp)){
		printf("%s", str);
	}
	pclose(fp);

	return 0;
}

zipオープンまではいくが、fgetsの出力がうまくいかんな。

C言語でlnコマンドを書きたい

■要求定義
– ファイルやフォルダにリンクを設定する

■lnコマンドの使い方
$ ls -l
total 16
-rw-rw-r– 1 vagrant vagrant 0 May 18 07:09 0516.txt
-rw-rw-r– 1 vagrant vagrant 0 May 18 07:09 0517.txt
-rw-rw-r– 1 vagrant vagrant 0 May 18 07:09 0518.txt
$ ln -s 0518.txt MuseumDay // ln -sでシンボリックリンクを作る
$ ls -l
total 28
-rw-rw-r– 1 vagrant vagrant 11 May 18 08:09 0516.txt
-rw-rw-r– 1 vagrant vagrant 11 May 18 08:09 0517.txt
-rw-rw-r– 1 vagrant vagrant 11 May 18 08:09 0518.txt
lrwxrwxrwx 1 vagrant vagrant 8 May 18 08:11 MuseumDay -> 0518.txt
$ cat MuseumDay // シンボリックリンクの中身を確認
abcde
12345
$ sudo vi MuseumDay
$ cat 0518.txt // シンボリックリンクを編集すると、ファイルが更新されている
abcde
12345
asdf

■検討
– symlinkでシンボリックリンクを作成する
#include
int symlink(const char *oldpath, const char *newpath);

#include <stdio.h>
#include <unistd.h>  // standard symbolic constants and types

int main(){

	if(symlink("./0517.txt", "NYSE") == 0){
		printf("シンボリックリンクを作成しました。\n");
	} else {
		printf("失敗しました。\n");
	}
	
	return 0;
}

$ ls -l
-rw-rw-r– 1 vagrant vagrant 11 May 18 08:09 0516.txt
-rw-rw-r– 1 vagrant vagrant 11 May 18 08:09 0517.txt
-rw-rw-r– 1 vagrant vagrant 17 May 18 08:14 0518.txt
lrwxrwxrwx 1 vagrant vagrant 8 May 18 08:11 MuseumDay -> 0518.txt
lrwxrwxrwx 1 vagrant vagrant 10 May 18 08:34 NYSE -> ./0517.txt

シンボリックリンクそのものは、ファイルまでのパスを含んだ小さなファイル
リダイレクトファイルみたいなものか??


Symbolic links were already present by 1978 in minicomputer operating systems from DEC and Data General’s RDOS.

■アルゴリズム
1. check: old_file must exist and new_file not yet exist
2. create new file, change new file to Link type
3. store old file name in newfile’s INODE.i_block [] area
set file size to length of old_file name
mark new file’s minode dirty
input newfile’s minode
4. mark new file parent minode dirty
input new file’s parent minode

inodeとは 主にUNIX系で使われる、ファイルやディレクトリの属性情報が書いてあるデータ

なるほど、inodeにリンク先の情報が書かれているわけだ。道理で中身を見れないわけだ。

C言語でhistoryコマンドを書きたい

■要求定義
– コマンドの履歴を表示する

■検討
– historyの使われ方
$ history | head // 過去のコマンド一覧
$ history 10 // 最近実行したコマンド

このコマンドの履歴は、.bash_historyに格納されている。catでも見ることができる。
$ cat .bash_history

#include <stdio.h>

int main(){

	FILE *fp;
	char str[1024];

	fp = fopen("./../.bash_history","r");

	if(fp == NULL){
		printf("failed.\n");
		return -1;
	}

	while((fgets(str,256,fp)) !=NULL){
		printf("%s", str);
	}
	fclose(fp);

	return 0;
}

historyはコマンドプロンプトの履歴だが、原理は.bash_historyを見ているだけなので、catコマンドと一緒
隠しファイルの理由の一つは、ユーザが誤って変更や削除しないようにする為とのこと

チーム開発で何かあった時に、.bash_historyを確認する、って工程は使えそうだな。