架设通过 SSH 和 HTTP 协议的 Git 服务器

本文描述如何架设通过 SSH 协议和 HTTP 协议的 Git 服务器,以 Ubuntu 操作系统为例。通过 SSH 协议方式 push,而 HTTP 方式则提供给匿名用户 clone。

这个方案可以通过用户和文件系统的权限很方便的控制项目的访问权限,开源还是闭源。

软件需求
1. git-core
2. openssh-server
3. apache2

服务器端
1. 安装 git-core

sudo apt-get install git-core

2. 创建 git 用户和组
可以使用密码认证,也可以使用证书。

sudo groupadd git
sudo useradd -d /home/git -m -g git git
sudo passwd git

3. 创建虚拟主机
这个不详细说了,没有什么特别设置。这里假设根目录是 /home/git/

4. 创建一个新的版本库

mkdir /home/git/test.git
cd /home/git/test.git
git --bare init
chmod +x hooks/post-update

客户机端
1. push 到远程版本库

mkdir test
cd test
git init
echo "test" > README
git add .
git commit -m 'Init.'
git remote add origin git@server_addr:test.git
git push origin master

2. clone 远程版本库

git clone http://server_addr/test.git

Over!

记录一个 Makefile

# Makefile for sphone
 
PP=cpp
CC=gcc
CCFLAGS=`pkg-config --cflags gtk+-2.0`
LDFLAGS=`pkg-config --libs gtk+-2.0`
 
SRCDIR=src
BINDIR=bin
BUILDDIR=build
 
TARGET=$(BINDIR)/sphone
CCOBJSFILE=$(BUILDDIR)/ccobjs
-include $(CCOBJSFILE)
LDOBJS=$(patsubst $(SRCDIR)%.c,$(BUILDDIR)%.o,$(CCOBJS))
 
DEPEND=$(LDOBJS:.o=.dep)
 
all : $(CCOBJSFILE) $(TARGET)
	@$(RM) $(CCOBJSFILE)
 
clean : 
	@echo -n "Clean ... " && $(RM) $(BINDIR)/* $(BUILDDIR)/* && echo "OK"
 
$(CCOBJSFILE) : 
	@echo CCOBJS=`ls $(SRCDIR)/*.c` > $(CCOBJSFILE)
 
$(TARGET) : $(LDOBJS)
	@echo -n "Linking $^ to $@ ... " && $(CC) -o $@ $^ $(LDFLAGS) && echo "OK"
 
$(BUILDDIR)/%.dep : $(SRCDIR)/%.c
	@$(PP) $(CCFLAGS) -MM -MT $(@:.dep=.o) -o $@ $<
 
$(BUILDDIR)/%.o : $(SRCDIR)/%.c
	@echo -n "Building $< ... " && $(CC) $(CCFLAGS) -c -o $@ $< && echo "OK"
 
-include $(DEPEND)

Over!

C 语言 CGI 访问计数器

用 C 语言写的简单的 CGI 访问计数器程序,用文件保存的计数,我觉得比数据库高效一点吧,呵呵。

/* counter.c
 * Heihaier <admin@heiher.info>
 */
 
#include <stdio.h>
#include <stdlib.h>
 
int main(int argc, char * argv[])
{
	unsigned long long int counter = 0;
	FILE * f = NULL;
	char file[256];
 
	printf("Content-type: text/html;\n\n");
 
	char * query = getenv("QUERY_STRING");
	if(NULL == query)
	  goto error;
 
	snprintf(file, 256, "./data/%s", query);
 
	f = fopen(file, "rw+");
	if(NULL == f)
	  goto error;
 
	fread(&counter, sizeof(unsigned long long int), 1, f);
 
	printf("document.write(\"<b>%llu</b>\");", ++counter);
 
	fseek(f, 0, SEEK_SET);
	fwrite(&counter, sizeof(unsigned long long int), 1, f);
 
	fclose(f);
 
	return 0;
error:
	return -1;
}
gcc counter.c -o /var/web/cgi-bin/counter.cgi
mkdir /var/web/cgi-bin/data
touch /var/web/cgi-bin/data/www.heiher.info
chmod 666 /var/web/cgi-bin/data/www.heiher.info

Over!

MySQL C 语言应用程序接口开发教程(2)

从数据库中取回数据
在这个实例中我们从表中取回数据。
步骤:

  • 创建连接
  • 执行查询
  • 获取结果集
  • 提取所有可用的记录
  • 释放结果集
#include <my_global.h>
#include <mysql.h>
 
int main(int argc, char * argv[])
{
	MYSQL * conn;
	MYSQL_RES * result;
	MYSQL_ROW row;
	int num_fields;
	int i;
 
	conn = mysql_init(NULL);
	mysql_real_connect(conn, "localhost", "username", "password", "testdb", 0, NULL, 0);
 
	mysql_query(conn, "SELECT * FROM writers");
	result = mysql_store_result(conn);
 
	num_fields = mysql_num_fields(result);
 
	while ((row = mysql_fetch_row(result)))
	{
		for (i=0; i<num_fields; i++)
		  printf("%s ", row[i] ? row[i] : "NULL");
		printf("\n");
	}
 
	mysql_free_result(result);
	mysql_close(conn);
}

实例程序打印 writers 表中所有的记录(姓名)。

./select
Leo Tolstoy
Jack London
Honore de Balzac
Lion Feuchtwanger
Emile Zola
mysql_query(conn, "SELECT * FROM writers");

执行查询将取回表 writers 中所有的记录。

result = mysql_store_result(conn);

取得结果集。

num_fields = mysql_num_fields(result);

获得表中的字段数量。

while ((row = mysql_fetch_row(result)))
{
	for (i=0; i<num_fields; i++)
	  printf("%s ", row[i] ? row[i] : "NULL");
	printf("\n");
}

获取记录并打印到屏幕。

mysql_free_result(result);

释放资源。

字段名称
这个实例里将要打印数据并显示字段名称。
为此我们创建一个新的表 friends。

mysql> CREATE TABLE friends (id int not null primary key auto_increment,
								name varchar(20), age int);
mysql> insert into friends(name, age) values('Tom', 25);
mysql> insert into friends(name, age) values('Elisabeth', 32);
mysql> insert into friends(name, age) values('Jane', 22);
mysql> insert into friends(name, age) values('Luke', 28);

插入一些数据到表中。

#include <my_global.h>
#include <mysql.h>
 
int main(int argc, char * argv[])
{
	MYSQL * conn;
	MYSQL_RES * result;
	MYSQL_ROW row;
	MYSQL_FIELD * field;
 
	int num_fields;
	int i;
 
	conn = mysql_init(NULL);
	mysql_real_connect(conn, "localhost", "username", "password", "testdb", 0, NULL, 0);
 
	mysql_query(conn, "SELECT * FROM friends");
	result = mysql_store_result(conn);
 
	num_fields = mysql_num_fields(result);
 
	while ((row = mysql_fetch_row(result)))
	{
		for(i=0; i<num_fields; i++)
		{
			if (i == 0) {
				while(field = mysql_fetch_field(result)) {
					printf("%s ", field->name);
				}
				printf("\n");
			}
			printf("%s ", row[i] ? row[i] : "NULL");
		}
	}
	printf("\n");
 
	mysql_free_result(result);
	mysql_close(conn);
 
	return 0;
}

这个实例和之前有一点差别,仅仅增加了字段名称。

while(field = mysql_fetch_field(result)) {
	printf("%s ", field->name);
}

mysql_fetch_field() 返回一个 MYSQL_FIELD 结构。我们从这个结构中得到名称(name)。

./header
id name age
1  Tom  25
2  Elisabeth  32
3  Jane  22
4  Luke  28

这是程序输出结果。

插件图片到 MySQL 数据库
一些用户喜欢将图片存入数据库,还有一些用户喜欢将图片存入本地文件系统。图片是二进制数据,MySQL 有专用的数据类型 BLOB(Binary Large Object) 以存储二进制数据。

mysql> describe images;
+-------+------------+------+-----+---------+-------+
| Field | Type       | Null | Key | Default | Extra |
+-------+------------+------+-----+---------+-------+
| id    | int(11)    | NO   | PRI |         |       |
| data  | mediumblob | YES  |     | NULL    |       |
+-------+------------+------+-----+---------+-------+
2 rows in set (0.00 sec)

这个表将在我们的实例中使用,通过下面的 SQL 语句可以创建它。

CREATE TABLE images(id int not null primary key, data mediumblob);
#include <my_global.h>
#include <mysql.h>
 
int main(int argc, char * argv[])
{
	MYSQL * conn;
 
	int len, size;
	char data[1000*1024];
	char chunk[2*1000*1024+1];
	char query[1024*5000];
 
	FILE * fp;
 
	conn = mysql_init(NULL);
	mysql_real_connect(conn, "localhost", "username", "password", "testdb", 0, NULL, 0);
 
	fp = fopen("image.png", "rb");
	size = fread(data, 1, 1024*1000, fp);
 
	mysql_real_escape_string(conn, chunk, data, size);
 
	char *stat = "INSERT INTO images(id, data) VALUES('1', '%s')";
	len = snprintf(query, sizeof(stat)+sizeof(chunk), stat, chunk);
 
	mysql_real_query(conn, query, len);
 
	fclose(fp);
 
	mysql_close(conn);
 
	return 0;
}

在这个实例中,我们插入一张图片到表 images 中,图片最大可以是 1MB。

fp = fopen("images.png", "rb");
size = fread(data, 1, 1024*1000, fp);

这里打开图片并读入数据集。

mysql_real_escape_string(conn, chunk, data, size);

二进制数据可以包含特殊字符,为了在 SQL 语句中不造成麻烦。我们必需避开它们。 mysql_real_escape_string() 函数将编码后的数据放入集合 chunk。这样,它们就可以是合法的语句了。这个函数还会在结尾增加一个 NULL 字符,这也是为什么集合 chunk 是集合 data两倍多一个字节。

char * stat = "INSERT INTO images(id, data) VALUES('1', '%s')";
len = snpritnf(query, sizeof(stat)+sizeof(chunk), stat, chunk);

这两行代码准备查询语句。

mysql_real_query(conn, query, len);

最后,我们执行语句。

从 MySQL 数据库中有选择的取出图片
在上一个实例中,我们在数据库中插入了图片。在本实例中,我们将有选择的取出这些插入的图片。

#include <my_global.h>
#include <mysql.h>
 
int main(int argc, char * argv[])
{
	MYSQL * conn;
	MYSQL_RES * result;
	MYSQL_ROW row;
 
	unsigned long * lengths;
	FILE * fp;
 
	conn = msyql_init(NULL);
	mysql_real_connect(conn, "localhost", "username", "password", "testdb", 0, NULL, 0);
 
	fp = fopen("image.png", "wb");
 
	mysql_query(conn, "SELECT data FROM images WHERE id=1");
	result = mysql_store_result(conn);
 
	row = mysql_fetch_row(result);
	lengths = mysql_fetch_lengths(result);
 
	fwrite(row[0], lengths[0], 1, fp);
	mysql_free_result(result);
 
	fclose(fp);
	mysql_close(conn);
 
	return 0;
}

这个实例中我们将数据库中 ID 为 1 的图片创建为文件 image.png

fp = fopen("image.png", "wb");

以可写的方式打开一个文件。

mysql_query(conn, "SELECT data FROM images WHERE id=1");

选择 ID 为 1 的图片。

row = mysql_fetch_row(result);

row 包含了原始数据。

lengths = mysql_fetch_lengths(result);

获取图片长度。

fwrite(row[0], lengths[0], 1, fp);

使用标准函数 fwrite() 将数据写入文件。

Over!

MySQL C 语言应用程序接口开发教程(1)

关于教程
这是一篇 MySQL 数据库的 C 语言开发教程。它讲述了用 C 语言开发 MySQL 应用程序的基本过程。

关于 MySQL 数据库
MySQL 是一个重要的开放源代码的、多用户的、多线程的数据库管理系统。MySQL 在 Web 应用中很流行。它是非常流行的 LAMP(Linux, Apache, MySQL, PHP) 架构的一部分。MySQL 被瑞典一家名为 MySQL AB 并对开源事业有杰出贡献的公司所有。MySQL 数据库可以运行在很多的操作系统上,如 BSD, Unix, Linux Windows 和 Mac。维基百科和 Youtube 都在使用 MySQL。这些站点一天有数百万将的查询。MySQL 有两个版本,分别是 MySQL 服务器系统和 MySQL 嵌入式系统。

第一个实例
我们第一个实例将要测试调用一个 MySQL 函数。

#include <my_global.h>
#include <mysql.h>
 
int main(int argc, char * argv[])
{
	printf("MySQL client version: %s\n", mysql_get_client_info());
	return 0;
}

函数 mysql_get_client_info()返回 MySQL 客户端版本信息。

gcc version.c -o version `mysql_config --cflags --libs`
./version

这是是如何编译和运行这个实例。

MySQL client version: 5.0.38
#include <my_global.h>
#include <mysql.h>

我们包含两个需要的头文件: mysql.h MySQL 函数调用的最重要的头文件。my_global.h 包含了一些全局的声明和标准输入/输出头文件。

printf("MySQL client version: %s\n", mysql_get_client_info());

这行代码输入了 MySQL 客户端的版本信息。我们调用了 mysql_get_client_info()函数。

创建一个数据库
在第二个实例中创建一个数据库。

#include <my_global.h>
#include <mysql.h>
 
int main(int argc, char * argv[])
{
	MYSQL * conn;
 
	conn = mysql_init(NULL);
 
	if (conn == NULL) {
		printf("Error %u: %s\n", mysql_errno(conn), mysql_error(conn));
		exit(1);
	}
 
	if (mysql_real_connect(conn, "localhost", "username", "password", NULL, 0, NULL, 0) == NULL) {
		printf("Error %u: %s\n", mysql_errno(conn), mysql_error(conn));
		exit(1);
	}
 
	if (mysql_query(conn, "CREATE DATABASE testdb")) {
		printf("Error %u: %s\n", mysql_errno(conn), mysql_error(conn));
		exit(1);
	}
 
	mysql_close(conn);
 
	return 0;
}

这段代码连接了 MySQL 数据库系统并创建了一个名为 testdb 的数据库。

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| testdb             |
+--------------------+
3 rows in set (0.00 sec)

检验的确创建了一个新的库。

在这段实例中我们包含了错误检测,检查可能出现的错误是非常重要的。在数据库开发领域里,很多地方都可能出错。为了有清晰的代码之后的实例中将不包含错误检查,我们假设所有人清楚错误检测是开发人员的责任

实例分为如下的部分:

  • 初始化一个连接对象结构
  • 创建一个连接
  • 执行一个查询
  • 关闭连接
MYSQL * conn;

我们声明一个 MYSQL 结构指针,这个结构是连接对象。

conn = mysql_init(NULL);

mysql_init() 函数返回一个连接对象。

if (conn == NULL) {
	printf("Error %u: %s\n", mysql_errno(conn), mysql_error(conn));
	exit(1);
}

我们检查返回值,如果 mysql_init() 调用失败打印错误消息并中止应用程序。

if (mysql_real_connect(conn, "localhost", "username", "password", NULL, 0, NULL, 0) == NULL) {
	printf("Error %u: %s\n", mysql_errno(conn), mysql_error(conn));
	exit(1);
}

mysql_real_connect() 函数建立了一个到数据库的连接。我们提供连接对象、主机名、用户名和密码参数。其它四个参数分别是数据库名、端口、Unix 套接字和客户端标记。

if (mysql_query(conn, "CREATE DATABASE testdb")) {
	printf("Error %u: %s\n", mysql_errno(conn), mysql_error(conn));
	exit(1);
}

mysql_query() 执行一个 SQL 语句。这里创建一个新的库。

mysql_close(conn);

最后,我们关闭数据库连接。

创建和操作表
第三个实例将要创建一个表并插入一些数据。

#include <my_global.h>
#include <mysql.h>
 
int main(int argc, char * argv[])
{
	MYSQL * conn;
 
	conn = mysql_init(NULL);
	mysql_real_connect(conn, "localhsot", "username", "password", "testdb", 0, NULL, 0);
 
	mysql_query(conn, "CREATE TABLE writers(name VARCHAR(25))");
 
	mysql_query(conn, "INSERT INTO writers VALUES('Leo Tolstoy')");
	mysql_query(conn, "INSERT INTO writers VALUES('Jack London')");
	mysql_query(conn, "INSERT INTO writers VALUES('Honore de Balzac')");
	mysql_query(conn, "INSERT INTO writers VALUES('Lion Feuchtwanger')");
	mysql_query(conn, "INSERT INTO writers VALUES('Emile Zole')");
 
	mysql_close(conn);
 
	return 0;
}

我们不使用任何新的 MySQL 函数。使用 mysql_query() 函数去创建表和插入数据。

mysql_real_connect(conn, "localhost", "username", "password", "testdb", 0, NULL, 0);

我们连接数据库 testdb,用户名 username 密码 password。

mysql_query(conn, "CREATE TABLE writers(name VARCHAR(25))");

这里我们创建一个名为 writers 的表,它有一个 varchar 类型的字段。

mysql_query(conn, "INSERT INTO writers VALUES('Leo Tolstoy')");

我们在 writers 表里插入一个记录。

mysql> show tables;
+------------------+
| Tables_in_testdb |
+------------------+
| writers          |
+------------------+
1 row in set (0.00 sec)

显示数据库里的表。

mysql> select * from writers;
+-------------------+
| name              |
+-------------------+
| Leo Tolstoy       |
| Jack London       |
| Honore de Balzac  |
| Lion Feuchtwanger |
| Emile Zola        |
+-------------------+
5 rows in set (0.00 sec)

枚举表中的记录。

Over!