分享
 
 
 

Linux程式设计之一

王朝system·作者佚名  2008-05-18
窄屏简体版  字體: |||超大  

Linux程式设计 - 1.fork

http://www.openchess.org/noitatsko/programming/ (2001-05-24 14:08:00)

在UNIX程式设计中,学会fork()的运用,算是相当基本的功夫。

fork()及signal经常运用在daemon守护神这一类常驻程式,另外像a4c.tty/yact/chdrv这些中文终端机程式也有用到,一般如Mozilla/Apache/Squid等大程式几乎都一定会用到。

--------------------------------------------------------------------------------

程序分歧fork()

fork()会产生一个与父程序相同的子程序,唯一不同之处在於其process id(pid)。

如果我们要撰写守护神程式,或是例如网路伺服器,需要多个行程来同时提供多个连线,可以利用fork()来产生多个相同的行程。

函数宣告

pid_t fork(void);

pid_t vfork(void);

返回值:

-1 : 失败。

0 : 子程序。

>0 : 将子程序的process id传回给父程序。

在Linux下fork()及vfork()是相同的东西。

Linux程式设计- 2.fork, pthread, and signals

http://www.openchess.org/noitatsko/programming/ (2001-05-24 15:00:00)

虽然在UNIX下的程式写作,对thread的功能需求并非很大,但thread在现代的作业系统中,几乎都已经存在了。pthread是Linux上的thread函数库,如果您要在Linux下撰写多线程式,例如MP3播放程式,熟悉pthread的用法是必要的。

有关thread写作,有两本很好的书:

Programming with POSIX Threads

Multithreading Programming Techniques

另外有一份初学者的参考文件Getting Started With POSIX Threads

pthread及signal都可以用一大章来讨论。在这里,我只谈及最简单及常用的技巧,当您熟悉这些基本技巧的运用後,再找一些专门深入探讨pthread及signal程式写作的书籍来研究。这些进阶的写法,用到的机会较少,将层次分明,学习速度应该会比较快。

--------------------------------------------------------------------------------

thread

我假设您对thread已经有一些基本的概念,因此,在此我将着重於如何实作。

函数宣告

int pthread_create(pthread_t * thread, pthread_attr_t * attr, void * (*start_routine)(void *), void * arg);

int pthread_join(pthread_t th, void **thread_return);

int pthread_detach(pthread_t th);

void pthread_exit(void *retval);

int pthread_attr_init(pthread_attr_t *attr);

资料结构

typedef struct

{

int detachstate;

int schedpolicy;

struct sched_param schedparam;

int inheritsched;

int scope;

} pthread_attr_t;

例一:

#include

#include

#include

#include

void * mythread(void *arg)

{

for (;;) {

printf("thread

");

sleep(1);

}

return NULL;

}

void main(void)

{

pthread_t th;

if (pthread_create(&th,NULL,mythread,NULL)!=0) exit(0);

for (;;) {

printf("main process

");

sleep(3);

}

}

执行结果:

./ex1

main process

thread

thread

thread

main process

thread

thread

thread

main process

thread

thread

thread

main process

Linux程式设计- 3.signals

http://www.openchess.org/noitatsko/programming/ (2001-05-24 16:47:48)

信号处理

--------------------------------------------------------------------------------

信号处理概说

送出信号

接收信号

信号的处理

任务控制

--------------------------------------------------------------------------------

POSIX IPC

reliable/unreliable

reentrant

pending

sending signals

catching signals

manipulating

signal definitions

--------------------------------------------------------------------------------

信号singals

信号的处理可以用一大章来写,涉及的层面也会深入整个作业系统中,我并不打算这样做,因为您可能会越搞越迷糊。这里我只告诉您如何接上信号,在实用的层面上,这样便很够用了。您可以先利用这些基本的技巧来撰写程式,等到有进一步高等应用的需要时,找一本较深入的UNIX Programming教材,专门研究signal的写法。

一般简单的signal写法如下:

void mysignal(int signo)

{

/* my signal handler */

}

void initsignal(void)

{

struct sigaction act;

act.sa_handler = mysignal;

act.sa_flags = 0;

sigemptyset(&act.sa_mask);

sigaction(SIGHUP,&act,NULL);

sigaction(SIGINT,&act,NULL);

sigaction(SIGQUIT,&act,NULL);

sigaction(SIGILL,&act,NULL);

sigaction(SIGTERM,&act,NULL);

}

例一: lock.c

在fork的例三中提到,在daemon被杀掉时,需要在离开前,将/var/run/lock.pid删除。这里我们可以利用signal来处理这件事。

#include

#include

#include

#include

#define LOCK_FILE "/var/run/lock.pid"

void quit(int signo)

{

printf("Receive signal %d

",signo);

unlink(LOCK_FILE);

exit(1);

}

void main(void)

{

FILE *fp;

pid_t pid;

struct sigaction act;

if (access(LOCK_FILE,R_OK)==0) {

printf("Existing a copy of this daemon!

");

exit(1);

}

pid = fork();

if (pid>0) {

printf("daemon on duty!

");

fp = fopen(LOCK_FILE,"wt");

fprintf(fp,"%d",pid);

fclose(fp);

} else

exit(0); if (pid<0) {

printf("Can't fork!

");

exit(-1);

}

act.sa_handler = quit;

act.sa_flags = 0;

sigemptyset(&act.sa_mask);

sigaction(SIGTERM,&act,NULL);

sigaction(SIGHUP,&act,NULL);

sigaction(SIGINT,&act,NULL);

sigaction(SIGQUIT,&act,NULL);

sigaction(SIGUSR1,&act,NULL);

sigaction(SIGUSR2,&act,NULL);

for (;;) {

sleep(3);

}

}

编译:

gcc -o ex1 lock.c

执行

./ex1

daemon on duty!

送信号

我们先找出该守护神程式的pid

PID=`cat /var/run/lock.pid`

接下来利用kill来送信号

kill $PID

Receive signal 15

程式将会结束,并且/var/run/lock.pid将会被删除掉,以便下一次daemon再启动。注意到如果quit函数内,没有放exit(),程式将永远杀不掉。

接下来送一些其它的信号试试看。

./ex1

PID=`cat /var/run/lock.pid`

kill -HUP $PID

Receive signal 1

您可以自行试试

kill -INT $PID

kill -QUIT $PID

kill -ILL $PID

.

.

.

等等这些信号,看看他们的结果如何。

信号的定义

在/usr/include/signum.h中有各种信号的定义

#define SIGHUP 1 &nb

Linux程式设计- 4.socket

http://www.openchess.org/noitatsko/programming/ (2001-05-24 17:24:26)

UNIX Socket Programming基本上是一本书名。Socket programming其实需要相当程度的基础,我不想在这包山包海地,如果您需要彻底研究,可以买这本书来看。在此我想提供一些简单的Server/Client两端的简单写法,让你有个起点,做为进一步研究的基础。很多涉及较复杂的内容的,我在这便不详细说明,您可以照本宣科,照抄着用,稍微熟悉时,再细细研究。

--------------------------------------------------------------------------------

Client

int sock_connect(char *domain,int port)

{

int white_sock;

struct hostent * site;

struct sockaddr_in me;

site = gethostbyname(domain);

if (site==NULL) return -2;

white_sock = socket(AF_INET,SOCK_STREAM,0);

if (white_sock<0) return -1;

memset(&me,0,sizeof(struct sockaddr_in));

memcpy(&me.sin_addr,site->h_addr_list[0],site->h_length);

me.sin_family = AF_INET;

me.sin_port = htons(port);

return (connect(white_sock,(struct sockaddr *)&me,sizeof(struct sockaddr))<0) ? -1 : white_sock;

}

要由Client向伺服器端要求连线的步骤,首先您必须要找出对方的位址,可利用:

gethostbyname()

接下来要建立起一个socket,然後用这个socket来建立连线。

接下来我们利用这个简单的socket程式来写一个读取WWW网页的简单浏览器(看html source)。

#include

#include

#include

#include

#include

#include

#include

int htconnect(char *domain,int port)

{

int white_sock;

struct hostent * site;

struct sockaddr_in me;

site = gethostbyname(domain);

if (site==NULL) return -2;

white_sock = socket(AF_INET,SOCK_STREAM,0);

if (white_sock<0) return -1;

memset(&me,0,sizeof(struct sockaddr_in));

memcpy(&me.sin_addr,site->h_addr_list[0],site->h_length);

me.sin_family = AF_INET;

me.sin_port = htons(port);

return (connect(white_sock,(struct sockaddr *)&me,sizeof(struct sockaddr))<0) ? -1 : white_sock;

}

int htsend(int sock,char *fmt,...)

{

char BUF[1024];

va_list argptr;

va_start(argptr,fmt);

vsprintf(BUF,fmt,argptr);

va_end(argptr);

return send(sock,BUF,strlen(BUF),0);

}

void main(int argc,char **argv)

{

int black_sock;

char bugs_bunny[3];

if (argc<2) return;

black_sock = htconnect(argv[1],80);

if (black_sock<0) return;

htsend(black_sock,"GET / HTTP/1.0%c",10);

htsend(black_sock,"Host: %s%c",argv[1],10);

htsend(black_sock,"%c",10);

while (read(black_sock,bugs_bunny,1)>0) { printf("%c",bugs_bunny[0]); }

close(black_sock);

}

编译:

gcc -o ex1 client.c

执行

./ex1 www.linux.org.tw

--------------------------------------------------------------------------------

Server

Listen to a port

要建立起一个网路伺服器,第一步就是要"倾远方",也就是要Listen。

以下是一般建立服务的方法:

int DaemonSocket;

struct sockaddr_in DaemonAddr;

int BindSocket(void)

{

DaemonSocket = socket(AF_INET,SOCK_STREAM,0);

if (DaemonSocket==-1) return 0;

DaemonAddr.sin_family = AF_INET;

DaemonAddr.sin_port = htons(DAEMON_PORT);

if (bind(DaemonSocket,&DaemonAddr,sizeof(DaemonAddr))<0) {

printf("Can not bind!

");

return 0;

}

if (listen(DaemonSocket,1024)!=0) {

printf("Can not listen!

");

return 0;

}

return 1;

}

Incoming call

要查看是否有连线进来,可用以下方式:

int incoming_call(void)

{

fd_set sock;

struct timeval tv;

int t;

FD_ZERO(&sock);

FD_SET(DaemonSocket,&sock);

tv.tv_sec = 60; tv.tv_usec = 0;

t = select(DaemonSocket + 1,&sock,NULL,NULL,&tv);

if (t<=0||!FD_ISSET(DaemonSocket,&sock)) return 0;

printf("incoming...

");

return 1;

}

Connect Client

当我们确认有人进来要求服务时,会需要accept connection,可用以下方式

int ConnectClient(void)

{

int socksize=sizeof(HostAddr);

unsigned char * addr;

ClientSocket = accept(DaemonSocket,(struct sockaddr*)&HostAddr,&socksize);

if (ClientSocket<0) return 0;

addr = (unsigned char *)&HostAddr.sin_addr.s_addr;

printf("incoming address:%d.%d.%d.%d

",addr[0],addr[1],addr[2],addr[3]);

return 1;

}

注意到当您accept connection之後,连线已建立起,此时要用的socket是ClientSocket,而非DaemonSocket,ClientSocket才是真正用来连线用的socket。

这是个我才刚开始动手写的象棋伺服器。

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#define DAEMON_LOCK "/var/chess/daemon.lock"

#define DAEMON_LOG "/var/chess/daemon.log"

#define DAEMON_PORT 9901

int DaemonSocket;

struct sockaddr_in DaemonAddr;

int ClientSocket=0;

struct sockaddr_in HostAddr;

void dlog(char *fmt,...)

{

va_list argptr;

FILE *fp;

fp = fopen(DAEMON_LOG,"a+t");

va_start(argptr,fmt);

vfprintf(fp,fmt,argptr);

va_end(argptr);

fclose(fp);

}

pid_t CheckLock(void)

{

pid_t me;

FILE * fp;

fp = fopen(DAEMON_LOCK,"rt");

if (fp==NULL) return 0;

fscanf(fp,"%d",&me);

fclose(fp);

return me;

}

pid_t WriteLock(void)

{

pid_t me;

FILE *fp;

me = getpid();

fp = fopen(DAEMON_LOCK,"w");

fprintf(fp,"%d",me);

fclose(fp);

return me;

}

int CleanLock(void)

{

return (unlink(DAEMON_LOCK)==0);

}

void report_time(void)

{

time_t now;

now = time(NULL);

dlog("%s",asctime((const struct tm*)localtime(&now)));

}

static void signal_catch(int signo)

{

time_t now;

close(DaemonSocket);

if (ClientSocket>0) close(ClientSocket);

CleanLock();

now = time(NULL);

dlog("Catch signal %d, leave at %s

",signo,asctime((const struct tm*)localti

exit(-1);

}

void SetupSignal(void)

{

struct sigaction act;

act.sa_handler = signal_catch;

act.sa_flags = 0;

sigemptyset(&act.sa_mask);

sigaction(SIGHUP,&act,NULL);

sigaction(SIGINT,&act,NULL);

sigaction(SIGQUIT,&act,NULL);

sigaction(SIGILL,&act,NULL);

sigaction(SIGABRT,&act,NULL);

sigaction(SIGIOT,&act,NULL);

sigaction(SIGBUS,&act,NULL);

sigaction(SIGFPE,&act,NULL);

sigaction(SIGTERM,&act,NULL);

}

int BindSocket(void)

{

DaemonSocket = socket(AF_INET,SOCK_STREAM,0);

if (DaemonSocket==-1) return 0;

DaemonAddr.sin_family = AF_INET;

DaemonAddr.sin_port = htons(DAEMON_PORT);

if (bind(DaemonSocket,&DaemonAddr,sizeof(DaemonAddr))<0) {

printf("Can not bind!

");

return 0;

}

if (listen(DaemonSocket,1024)!=0) {

printf("Can not listen!

");

return 0;

}

return 1;

}

int incoming_call(void)

{

fd_set sock;

struct timeval tv;

int t;

FD_ZERO(&sock);

FD_SET(DaemonSocket,&sock);

tv.tv_sec = 60; tv.tv_usec = 0;

t = select(DaemonSocket + 1,&sock,NULL,NULL,&tv);

if (t<=0||!FD_ISSET(DaemonSocket,&sock)) return 0;

dlog("incoming...

");

return 1;

}

int ConnectClient(void)

{

int socksize=sizeof(HostAddr);

unsigned char * addr;

ClientSocket = accept(DaemonSocket,(struct sockaddr*)&HostAddr,&socksize);

if (ClientSocket<0) return 0;

addr = (unsigned char *)&HostAddr.sin_addr.s_addr;

dlog("incoming address:%d.%d.%d.%d

",addr[0],addr[1],addr[2],addr[3]);

return 1;

}

int daemon_printf(char *fmt,...)

{

char BUF[4096];

va_list argptr;

va_start(argptr,fmt);

vsprintf(BUF,fmt,argptr);

va_end(argptr);

return write(ClientSocket,BUF,strlen(BUF));

}

void Log(void)

{

char BUF[4096];

read(DaemonSocket,BUF,16);

daemon_printf("%s",BUF[0]);

}

int main(int argc,char **argv)

{

pid_t myself;

time_t now;

/* find myself */

myself = CheckLock();

if (myself!=0) {

printf("Existing a copy of chess daemon[pid=%d], leave now.

",myself);

exit(1);

}

/* fork */

myself = fork();

if (myself>0) {

exit(1);

} else

if (myself<0) {

printf("Strange world! I don't like it. Quit because of pid=%d

",myself);

exit(1);

} else {

SetupSignal();

if (!BindSocket()) {

printf("Can not bind socket!

");

exit(1);

}

WriteLock();

}

printf("Chess Daemon is up, have fun!

");

now = time(NULL);

dlog("----------------------------------------------

");

dlog(

"I am back! %s"

"Chess Daemon comes to alive again.

",

asctime((const struct tm*)localtime(&now))

);

do {

if (incoming_call()) {

if (ConnectClient()) {

fd_set sock;

struct timeval tv;

int t;

char BUF[128];

char CC[2];

int n;

daemon_printf("Welcome to Chinese Chess Game Center!

");

FD_ZERO(&sock);

FD_SET(ClientSocket,&sock);

n = 0;

do {

tv.tv_sec = 60; tv.tv_usec = 0;

t = select(ClientSocket+1,&sock,NULL,NULL,&tv);

if (t<=0||!FD_ISSET(ClientSocket,&sock)) ;

read(ClientSocket,CC,1);

if (CC[0]==13||CC[0]==10||CC[0]==0) {

BUF[n] = 0;

dlog("%s

",BUF);

if (strncasecmp(BUF,"exit",4)==0) {

close(ClientSocket);

break;

}

n = 0;

} else {

BUF[n]=CC[0]; n++;

}

} while (1);

}

}

} while (1);

return 1;

}

检验

telnet localhost 9901

在处理Connect Client时,事实上可以运用fork或thread来处理多个连线。

Linux程式设计- 5.inetd

http://www.openchess.org/noitatsko/programming/ (2001-05-24 18:08:00)

inetd提供被动式的伺服器服务,也就是伺服器是被使用端所启动,平时则无须存在。例如,ftp, telnetd, pop3,imap, auth等等,这些服务没有人使用时,无须启动。此外,inetd将socket转换成stdin/stdout,因而使得网路服务程式设计大大简化,您可以只用printf及fgets便可完成处理很复杂的网路协定。

--------------------------------------------------------------------------------

inetd programming

利用inetd来做网路程式设计是个既简单又稳定的设计方法,您不需要考虑到复杂的socket programming。您的设计工作几乎在设计好通讯协定後就完成了,所需要的技巧,仅为简单的文字分析技巧。

goodie inet service

首先,我们先来撰写一个称为goodie的服务程式。

goodie.c

#include

#include

#include

void main(void)

{

printf("Welcome to goodie service!

");

}

这个程式很简单,不是吗?

编译

gcc -o goodie goodie.c

设定/etc/services及/etc/inetd.conf

在/etc/services中加入以下这一行

goodie 20001/tcp

其意义为goodie这项服务是在port 20001、TCP协定。

接下来在/etc/inetd.conf中加入以下这一行

goodie stream tcp nowait root /full_goodie_path_name/goodie

各项参数的意义为

service_name需要为在services中存在的名称。

sock_type有很多种,大多用的是stream/dgram。

proto一般用tcp/udp。

flags有wait/nowait。

user是您指定该程式要以那一个使用者来启动,这个例子中用的是root,如果有安全性的考量,应该要改用nobody。一般来说,建议您用低权限的使用者,除非必要,不开放root使用权。

server_path及args,这是您的服务程式的位置及您所想加入的参数。

接下来重新启动inetd

killall inetd

inetd

这样我们便建立起一个port 20001的goodie service。

现在我们来检验一下goodie是否可以执行:

telnet localhost 20001

telnet your_host_name 20001

执行结果

Trying 127.0.0.1...

Connected to localhost.

Escape character is '^]'.

Welcome to goodie service!

Connection closed by foreign host.

很简单不是吗? 信不信由您,telnet/pop3/imap/ftp都是靠这种方式建立起来的服务。

我们现在来建立一点小小的"网路协定",这个协定使我们可以输入"exit"时,离开程式,而其他的指令都是输出与输入相同的字串。

#include

#include

#include

void main(void)

{

char buf[1024];

int ok;

printf("Welcome to goodie service!

");

fflush(stdout);

ok=0;

do {

while (fgets(buf,1023,stdin)==NULL);

if (strncasecmp(buf,"exit",4)==0) ok=1;

printf(buf);

fflush(stdout);

} while (!ok);

}

执行结果

telnet localhost 20001

telnet your_host_name 20001

Trying 127.0.0.1...

Connected to localhost.

Escape character is '^]'.

Welcome to goodie service!

输入"help"

help

help

输入"exit"

exit

exit

Connection closed by foreign host.

接下来,我们将设计一个稍微复杂一点点的通讯协定,比较通用於一般用途。

#include

#include

#include

char *cmds[]={

"help",

"say",

"hello",

"bye",

"exit",

NULL

};

int getcmd(char *cmd)

{

int n=0;

while (cmds[n]!=NULL) {

if (strncasecmp(cmd,cmds[n],strlen(cmds[n]))==0) return n;

n++;

}

return -1;

}

void main(void)

{

char buf[1024];

int ok;

printf("Welcome to goodie service!

");

fflush(stdout);

ok=0;

do {

while (fgets(buf,1023,stdin)==NULL);

switch (getcmd(buf)) {

case -1: printf("Unknown command!

"); break;

case 0: printf("How may I help you, sir?

"); break;

case 1: printf("I will say %s",&buf[3]); break;

case 2: printf("How're you doing today?

"); break;

case 3: printf("Si ya, mate!

"); ok=1; break;

case 4: printf("Go ahead!

"); ok=1; break;

}

fflush(stdout);

} while (!ok);

}

telnet localhost 20001

telnet your_host_name 20001

试试看输入"help"、"say"、"hello"、"bye"、"exit"等等指令,及其它一些不在命令列中的指令。

在设计inetd服务程式时,要特别注意buffer overflow的问题,也就是以下这种状况:

char buffer_overflow[64];

fscanf(stdin,"%s",buffer_overflow);

历来几乎所有的安全漏洞都是由此而来的。

你一定不可这样用,不论任何理由,类同的用法也不可以。Cracker可以透过将您的buffer塞爆,然後塞进他自己的程式进来执行。

Linux程式设计- 6.syslog

http://www.openchess.org/noitatsko/programming/ (2001-05-24 19:00:00)

在Linux下有个syslogd的Daemon程式,syslog是个系统管理员必看的档案。因此,如果您的程式有除错或安全讯息要显示,写到syslog是个很好的选择。

syslog有三个函数,使用上,一般您只需要用syslog(...)这个函数即可,一般使用状况下,openlog/closelog是可有可无的。

syslog()中的priority是facility及level的组合,其後参数的用法与printf无异。

例:

#include

#include

#include

#include

void main(void)

{

if (fork()==0) {

for (;;) {

syslog(LOG_USER|LOG_INFO,"syslog programming test

");

sleep(10);

}

}

}

检验:

tail -f /var/log/messages

Mar 22 01:42:51 foxman log: syslog programming test

Mar 22 01:43:31 foxman last message repeated 4 times

Mar 22 01:44:31 foxman last message repeated 6 times

Mar 22 01:45:31 foxman last message repeated 6 times

Mar 22 01:46:21 foxman last message repeated 5 times

--------------------------------------------------------------------------------

void openlog( char *ident, int option, int facility)

void syslog( int priority, char *format, ...)

void closelog( void )

option

用於openlog()的option参数可以是以下几个的组合:

LOG_CONS : 如果送到system logger时发生问题,直接写入系统console。

LOG_NDELAY : 立即开启连接(通常,连接是在第一次写入讯息时才打开的)。

LOG_PERROR : 将讯息也同时送到stderr

LOG_PID : 将PID含入所有讯息中

facility

facility参数用来指定何种程式在记录讯息,这可让设定档来设定何种讯息如何处理。

LOG_AUTH : 安全/授权讯息(别用这个,请改用LOG_AUTHPRIV)

LOG_AUTHPRIV : 安全/授权讯息

LOG_CRON : 时间守护神专用(cron及at)

LOG_DAEMON : 其它系统守护神

LOG_KERN : 核心讯息

LOG_LOCAL0到LOG_LOCAL7 : 保留

LOG_LPR : line printer次系统

LOG_MAIL : mail次系统

LOG_NEWS : USENET news次系统

LOG_SYSLOG : syslogd内部所产生的讯息

LOG_USER(default) : 一般使用者等级讯息

LOG_UUCP : UUCP次系统

level

决定讯息的重要性. 以下的等级重要性逐次递减:

LOG_EMERG : 系统无法使用

LOG_ALERT : 必须要立即采取反应行动

LOG_CRIT : 重要状况发生

LOG_ERR : 错误状况发生

LOG_WARNING : 警告状况发生

LOG_NOTICE : 一般状况,但也是重要状况

LOG_INFO : 资讯讯息

LOG_DEBUG : 除错讯息

Linux程式设计- 7.zlib的运用

http://www.openchess.org/noitatsko/programming/ (2001-05-24 20:10:00)

gzip(*.gz)档案格式几乎是Linux下的标准格式了,有人认为bzip2的压缩率比gzip来得高。一般来说,这个说法大致正确,不过根据我个人的经验,有一半以上的档案,bzip2没有比gzip的压缩率来得高,有少数状况下,gzip压缩率反而比bzip2来的高。

zlib是个支援gzip档案格式的函数库,它使得gz档的存取就犹如开档关档一样地容易,您可以很容易地为您的程式加入gz档的支援。

--------------------------------------------------------------------------------

使用例 : showgz.c

#include

#include

#include

void main(int argc,char **argv)

{

gzFile zip;

int c;

if (argc<2) return;

zip = gzopen(argv[1],"rb");

while ((c=gzgetc(zip))!=EOF) putchar(c);

gzclose(zip);

}

编译

gcc -o showgz showgz.c -lz

检验

gzip -9 < showgz.c > showgz.c.gz

./showgz showgz.c.gz

将会把这个程式内容显示出来,showgz的作用可说等於gzip -dc。

--------------------------------------------------------------------------------

函数宣告

gzFile gzopen (const char *path, const char *mode);

开启一个gzip(*.gz)档。

mode参数可为"rb"或"wb"。

另外也可包含压缩程度如"wb9"。

用'f'作为过滤资料,如"wb6f"。

用'h'可指定Huffman only压缩,如"wb1h"

gzopen亦可用於读取非压缩的gzip档案格式,在这种状况下,gzread会直接读取,而不进行解压缩。

int gzread (gzFile file, voidp buf, unsigned len);

与read的用法相同。

int gzwrite (gzFile file, const voidp buf, unsigned len);

与write用法相同。

int gzprintf (gzFile file, const char *format, ...);

与fprintf用法相同。

char * gzgets (gzFile file, char *buf, int len);

与fgets用法相同。

int gzputc (gzFile file, int c);

与fputc用法相同。

int gzgetc (gzFile file);

与fgetc用法相同。

int gzflush (gzFile file, int flush);

与fflush作用相同。

z_off_t gzseek (gzFile file, z_off_t offset, int whence);

whence不支援SEEK_END

如果档案是开启为"读取",则SEEK_SET及SEEK_CUR,向前及向後均支援,不过很慢就是了。

如果档案是开启为"写入",仅支援向前SEEK。

int gzrewind (gzFile file);

与gzseek(file, 0L, SEEK_SET)相同作用,仅在读取时有效。

z_off_t gztell (gzFile file);

返回值 : 目前档案位置(解压缩後的位置)

int gzeof (gzFile file);

返回值 : 1 - EOF, 0 - not EOF

int gzclose (gzFile file);

关闭档案

返回值 : zlib error number

Linux程式设计- 8.crypt

http://www.openchess.org/noitatsko/programming/ (2001-05-24 21:04:00)

crypt是个密码加密函数,它是基於Data Encryption Standard(DES)演算法。crypt基本上是One way encryption,因此它只适用於密码的使用,不适合於资料加密。

char *crypt(const char *key, const char *salt);

key是使用者的密码。salt是两个字,每个字可从[a-zA-Z0-9./]中选出来,因此同一密码增加了4096种可能性。透过使用key中每个字的低七位元,取得56-bit关键字,这56-bit关键字被用来加密成一组字,这组字有13个可显示的ASCII字,包含开头两个salt。

crypt在您有自行管理使用者的场合时使用,例如会员网站、BBS等等。

--------------------------------------------------------------------------------

例一 : crypt_word.c

#include

#include

#include

void main(int argc,char **argv)

{

if (argc!=3) exit(0);

printf("%s

",crypt(argv[1],argv[2]));

}

编译

gcc -o crypt_word crypt.c -lcrypt

检验

请先看您的/etc/passwd,找一个您自己的帐号,看前面两个字,那是您自己的salt。接下来输入:

./crypt_word your_password salt

看看它们是否相同(应该要相同,除非您加了crypt plugin或使用不同的crypt function,例如shadow、pam,这种状况下,加密字是不同的),另外检验看看他们是否为13个字。

您也可以利用Apache上所附的htpasswd来产生加密字做为验。

例二: verify_passwd.c

注意,这个例读取/etc/passwd的资料,不适用於使用shadow或已经使用pam的系统(例如slackware,RedHat及Debian在不外加crypt plugin的状况下,应当相同)。此例仅供参考,做为解crypt函数运作的情形,真正撰写程式时,应该避免类似的写法。

#include

#include

#include

typedef struct {

char username[64];

char passwd[16];

int uid;

int gid;

char name[256];

char root[256];

char shell[256];

} account;

/* 注意! 以下的写法,真实世界的软体开发状况下,千万不要用! */

int acc_info(char *info,account *user)

{

char * start = info;

char * now = info;

/* username */

while (*now&&*now!=':') now++; /* 这是超级大安全漏洞 */

if (!*now) return 0;

*now = 0; now++;

strcpy(user->username,start); /* 这会导致buffer overflow */

start = now;

/* passwd */

while (*now&&*now!=':') now++; /* 这是超级大安全漏洞 */

if (!*now) return 0;

*now = 0; now++;

strcpy(user->passwd,start); /* 这会导致buffer overflow */

start = now;

/* uid */

while (*now&&*now!=':') now++;

if (!*now) return 0;

*now = 0; now++;

user->uid = atoi(start);

start = now;

/* gid */

while (*now&&*now!=':') now++;

if (!*now) return 0;

*now = 0; now++;

user->gid = atoi(start);

start = now;

/* name */

while (*now&&*now!=':') now++; /* 这是超级大安全漏洞 */

if (!*now) return 0;

*now = 0; now++;

strcpy(user->name,start); /* 这会导致buffer overflow */

start = now;

/* root */

while (*now&&*now!=':') now++; /* 这是超级大安全漏洞 */

if (!*now) return 0;

*now = 0; now++;

strcpy(user->root,start); /* 这会导致buffer overflow */

start = now;

/* shell */

while (*now&&*now!=':') now++; /* 这是超级大安全漏洞 */

*now = 0; now++;

strcpy(user->shell,start); /* 这会导致buffer overflow */

start = now;

return 1;

}

int read_password(char *filename,account *users)

{

FILE *fp;

char buf[1024];

int n;

n = 0;

fp = fopen(filename,"rt");

while (fgets(buf,1024,fp)!=NULL) {

if (acc_info(buf,&users[n])) n++;

}

fclose(fp);

return n;

}

void main(int argc,char **argv)

{

int n,i,done;

account ACC[128];

char username[256];

char password[256];

char * passwd;

char salt[4];

if (argc<2) {

printf("username:");

scanf("%s",username); /* 这是超级大安全漏洞 */

} else strcpy(username,argv[1]); /* 这是超级大安全漏洞 */

if (argc<3) {

printf("password:");

scanf("%s",password); /* 这是超级大安全漏洞 */

} else strcpy(password,argv[2]); /* 这是超级大安全漏洞 */

n = read_password("/etc/passwd",ACC);

for (i=0,done=0;i if (strcmp(username,ACC[i].username)==0) {

salt[0] = ACC[i].passwd[0];

salt[1] = ACC[i].passwd[1];

salt[2] = 0;

passwd = crypt(password,salt);

printf("%s %s %s

",ACC[i].username,ACC[i].passwd,passwd);

if (strcmp(passwd,ACC[i].passwd)==0) {

printf("login successfully!

");

} else {

printf("incorrect password!

");

}

done = 1;

}

if (!done) printf("invalid username!

");

}

编译

gcc -o verify_passwd verify_passwd.c -lcrypt

检验

./verify_passwd your_username your_password

避免安全漏洞

buffer overflow是个很严重的安全漏洞,通常您不可使用像char buf[xxxx]的宣告。在这一类与安全有相关的任何程式写作中(不是只有密码,例如像www/ftp/telnet的这一类对外窗口都要算在内),您应该要先检查字串长度。例如以下例子:

len = strlen(incoming_username);

if (len>xxx) invalid;

new_string = (char*)malloc(len+1);

strcpy(new_string,incoming_username);

your_own_operations...

如此才能避免buffer overflow,万万不可滥做假设,切记切记,连许多数十年经验丰富的老手都会犯这个错误。

--------------------------------------------------------------------------------

与crypt函数相关者尚有以下三个:

void setkey (const char *key);

void encrypt (char *block, int edflag);

void swab (const char *from, char *to, ssize_t n);

一般来说,除非您有特殊需求,你不会用到这三个。

Linux程式设计- 9.PAM

http://www.openchess.org/noitatsko/programming/ (2001-05-24 22:08:00)

Linux-PAM stands for Pluggable Authentication Modules for Linux。

PAM是个可外挂式的认模组。其详细文件一般在/usr/doc/pam-XX中,您也可以在metalab.unc.edu/LDP或Red Hat站中找到PAM-HOWTO。

我不准备介绍PAM的使用,在此我将精力放在如何运用PAM的函数库上。您在进一步看下去之前,应当先阅读有关PAM的相关资料,并且先解其运作机制,对它先有个初步解,然後再回来继续。

Linux程式设计- 10.termios/keymap/terminal programming

http://www.openchess.org/noitatsko/programming/ (2001-05-25 07:00:00)

termios

int tcgetattr (int fd, struct termios *termios_p);

int tcsetattr (int fd, int optional_actions,const struct termios *termios_p);

--------------------------------------------------------------------------------

keymap

我写了一个小程式来专门处理Linux上的keymap,keymap.h及keymap.c。

在Linux Terminal上,如果您想要设定某些按键返回特定值,您会需要用到以下这些技巧。

设定keymap

#include

#include

void setkeymap(void)

{

struct kbentry KEYMAP;

KEYMAP.kb_table=STATE;

KEYMAP.kb_index=SCANCODE;

KEYMAP.kb_value=VALUE;

ioctl(console,KDSKBENT,&KEYMAP);

}

STATE为状态键组合

/usr/include/linux/keyboard.h中

#define KG_SHIFT 0

#define KG_CTRL 2

#define KG_ALT 3

#define KG_ALTGR 1

#define KG_SHIFTL 4

#define KG_SHIFTR 5

#define KG_CTRLL 6

#define KG_CTRLR 7

#define KG_CAPSSHIFT 8

使用方式如:

#define KST_CTRL (1<#define KST_ALT (1<#define KST_SHIFT (1<#define KST_CTRL_ALT (KST_CTRL|KST_ALT)

#define KST_ALT_SHIFT (KST_ALT|KST_SHIFT)

SCANCODE为键盘扫描码

#define SCAN_ESC 0x01

#define SCAN_1 0x02

#define SCAN_2 0x03

#define SCAN_3 0x04

#define SCAN_4 0x05

#define SCAN_5 0x06

#define SCAN_6 0x07

#define SCAN_7 0x08

#define SCAN_8 0x09

#define SCAN_9 0x0A

#define SCAN_0 0x0B

#define SCAN_MINUS 0x0C

#define SCAN_PLUS 0x0D

#define SCAN_BACK 0x0E

#define SCAN_TAB 0x0F

#define SCAN_Q 0x10

#define SCAN_W 0x11

#define SCAN_E 0x12

#define SCAN_R 0x13

#define SCAN_T 0x14

#define SCAN_Y 0x15

#define SCAN_U 0x16

#define SCAN_I 0x17

#define SCAN_O 0x18

#define SCAN_P 0x19

#define SCAN_LTQUOTE 0x1A

#define SCAN_RTQUOTE 0x1B

#define SCAN_ENTER 0x1C

#define SCAN_CTRL 0x1D

#define SCAN_A 0x1E

#define SCAN_S 0x1F

#define SCAN_D 0x20

#define SCAN_F 0x21

#define SCAN_G 0x22

#define SCAN_H 0x23

#define SCAN_J 0x24

#define SCAN_K 0x25

#define SCAN_L 0x26

#define SCAN_SPLIT 0x27

#define SCAN_QUOTE 0x28

#define SCAN_MARK 0x29

#define SCAN_LSHIFT 0x2A

#define SCAN_STAND 0x2B

#define SCAN_Z 0x2C

#define SCAN_X 0x2D

#define SCAN_C 0x2E

#define SCAN_V 0x2F

#define SCAN_B 0x30

#define SCAN_N 0x31

#define SCAN_M 0x32

#define SCAN_LSQUOTE 0x33

#define SCAN_RSQUOTE 0x34

#define SCAN_QUESTION 0x35

#define SCAN_RSHIFT 0x36

#define SCAN_PRTSCR 0x37

#define SCAN_ALT 0x38

#define SCAN_SPACE 0x39

#define SCAN_CAPSLOCK 0x3A

#define SCAN_F1 0x3B

#define SCAN_F2 0x3C

#define SCAN_F3 0x3D

#define SCAN_F4 0x3E

#define SCAN_F5 0x3F

#define SCAN_F6 0x40

#define SCAN_F7 0x41

#define SCAN_F8 0x42

#define SCAN_F9 0x43

#define SCAN_F10 0x44

#define SCAN_NUMLOCK 0x45

#define SCAN_HOME 0x47

#define SCAN_UP 0x48

#define SCAN_PGUP 0x49

#define SCAN_LEFT 0x4B

#define SCAN_RIGHT 0x4D

#define SCAN_END 0x4F

#define SCAN_DOWN 0x50

#define SCAN_PGDN 0x51

#define SCAN_INSERT 0x52

#define SCAN_DELETE 0x53

#define SCAN_F11 0x85

#define SCAN_F12 0x86

/usr/include/linux/kd.h中

struct kbentry {

unsigned char kb_table;

unsigned char kb_index;

unsigned short kb_value;

};

#define KDGKBENT 0x4B46 /* gets one entry in translation table */

#define KDSKBENT 0x4B47 /* sets one entry in translation table */

而console为

console = open("/dev/console",O_RDWR);

读取按键

read(console,&c,sizeof(char));

--------------------------------------------------------------------------------

terminal programming

term.h/term.c是我写来专门处理一些小型的互动界面程式。

Terminal指令集

设定颜色 :

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有