SSH using a SOCKS or HTTP proxy

If you follow planet debian, you may already know about the ProxyCommand directive in $HOME/.ssh/config. It allows OpenSSH to connect to a remote host through a given command.

One setup that I use a lot is to have connections be established through a SOCKS proxy. Until today, I was using connect, a small tool written by Shun-ichi Gotô. The typical setup I used is:

    Host *.mydomain.com
    ProxyCommand connect -S socksserver:1080 %h %p

I also use jump hosts occasionally, with a setup like this:

    Host somehost.mydomain.com
    ProxyCommand ssh otherhost.mydomain.com nc -w1 %h %p

And today I discovered that netcat-openbsd does support connexions through a proxy, either SOCKS or HTTP. Why keep using two different tools when you can use one? 😉 So I changed my setup to:

    Host *.mydomain.com
    ProxyCommand nc -xsocksserver:1080 -w1 %h %p

The default is to use SOCKS 5, add -X4 for SOCKS 4 and -Xconnect for HTTP CONNECT proxies. Note that it doesn’t support choosing which end does the name resolutions like connect does with the -R option.

From: http://glandium.org/blog/?p=223

Over!

Sharing Mounts

Occasionally, certain system administration tasks require access to the same file system from more than one place in the directory tree (for example, when preparing a chroot environment). To address such requirements, the mount command implements the –bind option that provides a means for duplicating certain mounts. Its usage is as follows:

mount --bind old_directory new_directory

Although the above command allows a user to access the file system from both places, it does not apply on the file systems that are mounted within the original directory. To include these mounts as well, type:

mount --rbind old_directory new_directory

Additionally, to provide as much flexibility as possible, Red Hat Enterprise Linux 5.8 implements the functionality known as shared subtrees. This feature allows you to use the following four mount types:

Shared Mount
A shared mount allows you to create an exact replica of a given mount point. When a shared mount is created, any mount within the original mount point is reflected in it, and vice versa. To create a shared mount, type the following at a shell prompt:

mount --make-shared mount_point

Alternatively, you can change the mount type for the selected mount point and all mount points under it:

mount --make-rshared mount_point

Example: Creating a Shared Mount Point
There are two places where other file systems are commonly mounted: the /media directory for removable media, and the /mnt directory for temporarily mounted file systems. By using a shared mount, you can make these two directories share the same content. To do so, as root, mark the /media directory as “shared”:

~]# mount --bind /media /media
~]# mount --make-shared /media

Then create its duplicate in /mnt by using the following command:

~]# mount --bind /media /mnt

You can now verify that a mount within /media also appears in /mnt. For example, if you have non-empty media in your CD-ROM drive and the /media/cdrom/ directory exists, run the following commands:

~]# mount /dev/cdrom /media/cdrom
~]# ls /media/cdrom
EFI  GPL  isolinux  LiveOS
~]# ls /mnt/cdrom
EFI  GPL  isolinux  LiveOS

Similarly, you can verify that any file system mounted in the /mnt directory is reflected in /media. For instance, if you have a non-empty USB flash drive that uses the /dev/sdc1 device plugged in and the /mnt/flashdisk/ directory is present, type:

~]# mount /dev/sdc1 /mnt/flashdisk
~]# ls /media/flashdisk
en-US  publican.cfg
~]# ls /mnt/flashdisk
en-US  publican.cfg

Slave Mount
A slave mount allows you to create a limited duplicate of a given mount point. When a slave mount is created, any mount within the original mount point is reflected in it, but no mount within a slave mount is reflected in its original. To create a slave mount, type the following at a shell prompt:

mount --make-slave mount_point

Alternatively, you can change the mount type for the selected mount point and all mount points under it:

mount --make-rslave mount_point

Example: Creating a Slave Mount Point
Imagine you want the content of the /media directory to appear in /mnt as well, but you do not want any mounts in the /mnt directory to be reflected in /media. To do so, as root, first mark the /media directory as “shared”:

~]# mount --bind /media /media
~]# mount --make-shared /media

Then create its duplicate in /mnt, but mark it as “slave”:

~]# mount --bind /media /mnt
~]# mount --make-slave /mnt

You can now verify that a mount within /media also appears in /mnt. For example, if you have non-empty media in your CD-ROM drive and the /media/cdrom/ directory exists, run the following commands:

~]# mount /dev/cdrom /media/cdrom
~]# ls /media/cdrom
EFI  GPL  isolinux  LiveOS
~]# ls /mnt/cdrom
EFI  GPL  isolinux  LiveOS

You can also verify that file systems mounted in the /mnt directory are not reflected in /media. For instance, if you have a non-empty USB flash drive that uses the /dev/sdc1 device plugged in and the /mnt/flashdisk/ directory is present, type:

~]# mount /dev/sdc1 /mnt/flashdisk
~]# ls /media/flashdisk
~]# ls /mnt/flashdisk
en-US  publican.cfg

Private Mount
A private mount allows you to create an ordinary mount. When a private mount is created, no subsequent mounts within the original mount point are reflected in it, and no mount within a private mount is reflected in its original. To create a private mount, type the following at a shell prompt:

mount --make-private mount_point

Alternatively, you can change the mount type for the selected mount point and all mount points under it:

mount --make-rprivate mount_point

Example: Creating a Private Mount Point
Taking into account the scenario in Example 2.4, “Creating a Shared Mount Point”, assume that you have previously created a shared mount point by using the following commands as root:

~]# mount --bind /media /media
~]# mount --make-shared /media
~]# mount --bind /media /mnt

To mark the /mnt directory as “private”, type:

~]# mount --make-private /mnt

You can now verify that none of the mounts within /media appears in /mnt. For example, if you have non-empty media in your CD-ROM drive and the /media/cdrom/ directory exists, run the following commands:

~]# mount /dev/cdrom /media/cdrom
~]# ls /media/cdrom
EFI  GPL  isolinux  LiveOS
~]# ls /mnt/cdrom
~]#

You can also verify that file systems mounted in the /mnt directory are not reflected in /media. For instance, if you have a non-empty USB flash drive that uses the /dev/sdc1 device plugged in and the /mnt/flashdisk/ directory is present, type:

~]# mount /dev/sdc1 /mnt/flashdisk
~]# ls /media/flashdisk
~]# ls /mnt/flashdisk
en-US  publican.cfg

Unbindable Mount
An unbindable mount allows you to prevent a given mount point from being duplicated whatsoever. To create an unbindable mount, type the following at a shell prompt:

mount --make-unbindable mount_point

Alternatively, you can change the mount type for the selected mount point and all mount points under it:

mount --make-runbindable mount_point

Example: Creating an Unbindable Mount Point
To prevent the /media directory from being shared, as root, type the following at a shell prompt:

~]# mount --bind /media /media
~]# mount --make-unbindable /media

This way, any subsequent attempt to make a duplicate of this mount will fail with an error:

~]# mount --bind /media /mnt
mount: wrong fs type, bad option, bad superblock on /media/,
       missing code page or other error
       In some cases useful info is found in syslog - try
       dmesg | tail  or so

From: https://access.redhat.com/knowledge/docs/en-US/Red_Hat_Enterprise_Linux/5/html/Deployment_Guide/sect-Using_the_mount_Command-Mounting-Bind.html

Over!

组播技术中IPv4地址到MAC地址的映射

以太网组播MAC地址

以太网传输单播IP报文的时候,目的MAC地址使用的是接收者的MAC地址。但是在传输组播数据包时,其目的地不再是一个具体的接收者,而是一个成员不确定的组,所以要使用组播MAC地址。

IPv4组播MAC地址
IANA规定,IPv4组播MAC地址的高24位为0x01005E,第25位为0,低23位为IPv4组播地址的低23位。IPv4组播地址与MAC地址的映射关系如图1-6所示。

图1-6 IPv4组播地址与MAC地址的映射关系

由于IPv4组播地址的高4位是1110,代表组播标识,而低28位中只有23位被映射到IPv4组播MAC地址,这样IPv4组播地址中就有5位信息丢失。于是,就有32个IPv4组播地址映射到了同一个IPv4组播MAC地址上,因此在二层处理过程中,设备可能要接收一些本IPv4组播组以外的组播数据,而这些多余的组播数据就需要设备的上层进行过滤了。

Over!

From: http://0bill0.blog.51cto.com/195306/300481

内存屏障是什么?

内存屏障是指“由于编译器的优化和缓存的使用,导致对内存的写入操作不能及时的反应出来,也就是说当完成对内存的写入操作之后,读取出来的可能是旧的内容”

内存屏障的分类
1. 编译器引起的内存屏障
2. 缓存引起的内存屏障
3. 乱序执行引起的内存屏障

编译器引起的内存屏障
我们都知道,从寄存器里面取一个数要比从内存中取快的多,所以有时候编译器为了编译出优化度更高的程序,就会把一些常用变量放到寄存器中,下次使用该变量的时候就直接从寄存器中取,而不再访问内存,这就出现了问题,当其他线程把内存中的值改变了怎么办?也许你会想,编译器怎么会那么笨,犯这种低级错误呢!是的,编译器没你想象的那么聪明!让我们看下面的代码:(代码摘自《独辟蹊径品内核》)

int flag=0;
 
void wait(){
    while ( flag == 0 )
        sleep(1000);
    ......
}
 
void wakeup(){
    flag=1;
}

这段代码表示一个线程在循环等待另一个线程修改flag。 Gcc等编译器在编译的时候发现,sleep()不会修改flag的值,所以,为了提高效率,它就会把某个寄存器分配给flag,于是编译后就生成了这样的伪汇编代码:

void wait(){
    movl  flag, %eax;
 
    while ( %eax == 0)
        sleep(1000);
}

这时,当wakeup函数修改了flag的值,wait函数还在傻乎乎的读寄存器的值而不知道其实flag已经改变了,线程就会死循环下去。由此可见,编译器的优化带来了相反的效果!

但是,你又不能说是让编译器放弃这种优化,因为在很多场合下,这种优化带来的性能是十分可观的!那我们该怎么办呢?有没有什么办法可以避免这种情况?答案必须是肯定的,我们可以使用关键字volatile来避免这种情况。

volatile int flag = 0;

这样,我们就能避免编译器把某个寄存器分配给flag了。

好,上面所描述这些,就叫做“编译器优化引起的内存屏障”,是不是懂了点什么?再回去看看概念?

缓存引起的内存屏障
好,既然寄存器能够引起这样的问题,那么缓存呢?我们都知道,CPU会把数据取到一个叫做cache的地方,然后下次取的时候直接访问cache,写入的时候,也先将值写入cache。

那么,先让我们考虑,在单核的情况下会不会出现问题呢?先想一下,单核情况下,除了CPU还会有什么会修改内存?对了,是外部设备的DMA!那么,DMA修改内存,会不会引起内存屏障的问题呢?答案是,在现在的体系结构中,不会。

当外部设备的DMA操作结束的时候,会有一种机制保证CPU知道他对应的缓存行已经失效了;而当CPU发动DMA操作时,在想外部设备发送启动命令前,需要把对应cache中的内容写回内存。在大多数RISC的架构中,这种机制是通过一写个特殊指令来实现的。在X86上,采用一种叫做总线监测技术的方法来实现。就是CPU和外部设备访问内存的时候都需要经过总线的仲裁,有一个专门的硬件模块用于记录cache中的内存区域,当外部设备对内存写入的时候,就通过这个硬件来判断下改内存区域是否在cache中,然后再进行相应的操作。

那么,什么时候才能产生cache引起的内存屏障呢?多CPU? 是的,在多CPU的系统里面,每个CPU都有自己的cache,当同一个内存区域同时存在于两个CPU的cache中时,CPU1改变了自己cache中的值,但是CPU2却仍然在自己的cache中读取那个旧值,这种结果是不是很杯具呢?因为没有访存操作,总线也是没有办法监测的,这时候怎么办?

对阿,怎么办呢?我们需要在CPU2读取操作之前使自己的cache失效,x86下,很多指令能做到这点,如lock前缀的指令,cpuid, iret等。内核中使用了一些函数来完成这个功能:mb(), rmb(), wmb()。用的也是以上那些指令,感兴趣可以去看下内核代码。

乱序执行引起的内存屏障
我们都知道,超标量处理器越来越流行,连龙芯都是四发射的。超标量实际上就是一个CPU拥有多条独立的流水线,一次可以发射多条指令,因此,很多允许指令的乱序执行,具体怎么个乱序方法,可以去看体系结构方面的书,这里只说内存屏障。

指令乱序执行了,就会出现问题,假设指令1给某个内存赋值,指令2从该内存取值用来运算。如果他们两个颠倒了,指令2先从内存中取值运算,是不是就错了?

对于这种情况,x86上专门提供了lfence,sfence,和mfence 指令来停止流水线:
lfence:停止相关流水线,知道lfence之前对内存进行的读取操作指令全部完成
sfence:停止相关流水线,知道lfence之前对内存进行的写入操作指令全部完成
mfence:停止相关流水线,知道lfence之前对内存进行的读写操作指令全部完成

好,将完这三种类型,再回去看看概念,清晰了么?如果还不明白,那就是我的表达能力太有限了,自己网上再搜搜把!

From: http://www.spongeliu.com/%E8%AF%AD%E8%A8%80%E5%AD%A6%E4%B9%A0/clanguage/memorybarrier/

Over!

Systemd socket activation and Ruby

For anyone who doesn’t know what systemd is:

systemd is a system and service manager for Linux, compatible with SysV and LSB init scripts. systemd provides aggressive parallelization capabilities, uses socket and D-Bus activation for starting services, offers on-demand starting of daemons, keeps track of processes using Linux cgroups, supports snapshotting and restoring of the system state, maintains mount and automount points and implements an elaborate transactional dependency-based service control logic.

http://www.freedesktop.org/wiki/Software/systemd

It’s quite similar to Apple’s launchd (used in OSX) and is fully utilizing powerful features of the latest Linux kernel. systemd is default init system in latest Fedora, openSUSE and Mandriva and is available for many other Linux distros as alternative boot solution. I hope Ubuntu’s upstart team will give up soon because having systemd on Ubuntu servers would be awesome. For more info and idea behind the project I recommend reading Lennart Poettering’s announcement

One of the great features of this init system is socket activation of system services. In short, services are lazily started when they’re actually needed. Systemd listens on the sockets for them and starts the services on first incoming connection, passing them the listening sockets. Started services just start accepting clients on these sockets (without calling socket()+bind()+listen()).

It appears that the protocol for passing sockets to service processes is very simple. Environment variable LISTEN_PID is set to the PID of the service process and another environment variable LISTEN_FDS is set to the number of listening sockets passed. Socket descriptors start from number 3 and are sequential. For example, LISTEN_FDS with value of 2 means process should accept connections on 2 sockets with descriptors 3 and 4.

I’ll show you how all this works on an example echo server written in ruby. The server will send back what it receives. Additionally it will send information telling if listening socket came from systemd or not to each new connected client.

But first we need to create the socket unit file that specifies where systemd should listen on behalf of our service. /etc/systemd/system/echo-server.socket file can look as simple as this:

[Socket]
ListenStream=8888

Next, we need service unit file that specifies what binary to start when connections start coming. /etc/systemd/system/echo-server.service file may look like this:

[Service]
ExecStart=/home/kill/.rvm/bin/ruby-1.9.2-p290 /home/kill/bin/echo-server.rb
User=kill
StandardOutput=syslog
StandardError=syslog

I have ruby 1.9.2 installed via RVM so I’m running my ruby script with RVM’s wrapper specifying full paths (remember init process runs as root). I’m also setting the user on whose behalf the process should be run and I’m asking systemd to log process’ stdout/stderr to syslog (simplifies debugging).

Now, the echo server (/home/kill/bin/echo-server.rb):

#!/usr/bin/env ruby
 
require 'socket'
 
SD_LISTEN_FDS_START = 3
 
from_systemd = false
 
if ENV['LISTEN_PID'].to_i == $$
  # use existing socket passed from systemd
  server_socket = Socket.for_fd(SD_LISTEN_FDS_START + 0)
  from_systemd = true
else
  # create new listening socket on port 8888
  server_socket = Socket.tcp_server_sockets(8888)
end
 
Socket.accept_loop(server_socket) do |client_socket, addr|
  client_socket.send("OHAI! systemd socket: #{from_systemd}\n", 0)
 
  while (data = client_socket.recv(1000)).size > 0
    client_socket.send(data.upcase, 0)
  end
end

Implementation is very simple, still I’m gonna explain it a little bit as it illustrates the use of systemd socket activation protocol and the fallback – normal way of creating server socket.

Like I mentioned earlier, descriptors of systemd passed sockets start with 3:

SD_LISTEN_FDS_START = 3

We check if LISTEN_PID points to our echo-server.rb process:

if ENV['LISTEN_PID'].to_i == $$

If so, we’re creating new Socket instance for existing descriptor (3). Socket unit file tells systemd to listen on one port only (8888) so we can assume there’s only one socket descriptor passed:

  # use existing socket passed from systemd
  server_socket = Socket.for_fd(SD_LISTEN_FDS_START + 0)

If LISTEN_PID doesn’t match our process we just create TCP socket the usual way:

else
  # create new listening socket on port 8888
  server_socket = Socket.tcp_server_sockets(8888)
end

Finally, in Socket.accept_loop(server_socket) do { … } we handle incoming clients.

From: http://ku1ik.com/2012/01/21/systemd-socket-activation-and-ruby.html

Over!

Gedit 3 Plugin Sample

Required bindings not yet included in vala:
Gedit >= 3.0 (gedit-3.0.vapi)
GtkSource View >= 3.0 (gtksourceview-3.0.vapi)
PeasGtk-1.0 gobject introspection package of your distro
…and Vala >= 0.11

Source & support files
A Gedit plugin is composed of just two files: a library (.so) and the plugin file definition.

Our example plugin will be contained in just one vala soure file, so with all the dependencies in place this should be the directory listing:

$ ls
gedit-3-example.plugin
gedit-3-example-plugin.vala
gedit-3.0.vapi
gtksourceview-3.0.vapi

In order to make the example really simple we decided to implement a very basic function: the plugin will just close an xml tag upon writing the ‘>’. Eg. if you write it will add the corresponding close tag.

Source code for the file: gedit-3-example-plugin.vala

using GLib;
 
namespace GeditPluginExample
{
        /*
         * This class will be instantiated and activated for each Gedit View
         */
        public class View : Gedit.ViewActivatable, Peas.ExtensionBase
        {
                public View ()
                {
                        GLib.Object ();
                }
 
                public Gedit.View view {
                         get; construct;
                }
 
                public void activate ()
                {
                        print ("View: activated\n");
                        view.key_release_event.connect (this.on_key_release);
                }
 
                public void deactivate ()
                {
                        print ("View: deactivated\n");
                        view.key_release_event.disconnect (this.on_key_release);
                }
 
                private bool on_key_release (Gtk.Widget sender, Gdk.EventKey event)
                {
                        if (event.str == ">") {
                                // Close the tag
                                Gedit.View view = (Gedit.View)sender;
                                Gtk.TextBuffer buffer = view.get_buffer ();
                                Gtk.TextIter end, start;
 
                                buffer.get_iter_at_mark (out end, (Gtk.TextMark) buffer.get_insert ());
                                if (end.backward_char ()) {
                                        start = end;
                                        if (start.backward_word_start ()) {
                                                string tag = "</%s>".printf (buffer.get_text (start, end, false));
 
                                                // add the closing tag
                                                buffer.begin_user_action ();
                                                buffer.insert_interactive_at_cursor (tag, -1, true);
                                                buffer.end_user_action ();
 
                                                // move cursor back
                                                buffer.get_iter_at_mark (out end, (Gtk.TextMark) buffer.get_insert ());
                                                end.backward_chars (tag.length);
                                                buffer.place_cursor (end);
                                        }
                                }
                        }
                        return true;
                }
        }
 
        /*
         * Plugin config dialog
         */
        public class Config : Peas.ExtensionBase, PeasGtk.Configurable
        {
                public Config () 
                {
                        Object ();
                }
 
                public Gtk.Widget create_configure_widget () 
                {
                        return new Gtk.Label (" Gedit 3.0 Example Vala Plugin ");
                }
        }
}
 
[ModuleInit]
public void peas_register_types (TypeModule module) 
{
        var objmodule = module as Peas.ObjectModule;
 
        // Register my plugin extension
        objmodule.register_extension_type (typeof (Gedit.ViewActivatable), typeof (GeditPluginExample.View));
        // Register my config dialog
        objmodule.register_extension_type (typeof (PeasGtk.Configurable), typeof (GeditPluginExample.Config));
}

Contents of the plugin definition file: gedit-3-example.plugin

[Plugin]
Module=gedit-3-example-plugin.so
IAge=2
Name=Vala Example Plugin
Description=A simple Vala Example Plugin
Authors=Andrea Del Signore <sejerpz@tin.it>
Copyright=Copyright © 2011 Andrea Del Signore
Website=http://live.gnome.org/action/Vala/Gedit3PluginSample

Compiling & Installing

$ valac --vapidir . -C gedit-3-example-plugin.vala --pkg gtk+-3.0 --pkg gedit-3.0 --pkg PeasGtk-1.0 --pkg GtkSource-3.0
$ gcc --shared -o libgedit-3-example-plugin.so gedit-3-example-plugin.c `pkg-config --cflags --libs gedit gtk+-3.0 gtksourceview-3.0 libpeas-gtk-1.0`
$ cp libgedit-3-example-plugin.so gedit-3-example.plugin ~/.local/share/gedit/plugins/

Running
Start GEdit 3 and enable the plugin from the edit -> preference menu

Over!

From: https://live.gnome.org/Vala/Gedit3PluginSample

Having pacman verify packages

For the past six months, pacman’s package verification features were turned off by default while we were figuring out the details of our public-key infrastructure.

They have finally been enabled in pacman-4.0.3-2; when you upgrade, you will be prompted to run:

pacman-key --init
pacman-key --populate archlinux

This sets up a local keyring for pacman, and populates it with the data needed to authenticate official packages. This includes five master keys used to authenticate Arch Linux packagers (developers and trusted users), so you do not need to know who joins or leaves the team: you only have to verify those five master keys once and for all. The populate command will prompt you to do so; please do this cautiously by checking the fingerprints displayed against those published on our website.

Then, merge your pacman.conf with pacman.conf.pacnew, that is, enable package verification through the SigLevel option, and you should be good to go.

For details on the development of pacman and archlinux-keyring, see the blog posts of Allan and Pierre.

Over!

From: http://www.archlinux.org/news/having-pacman-verify-packages/

Vala D-Bus Examples

Vala supports D-Bus inter-process communication using the GDBus API that is part of GLib/GIO since version 2.26.

Vala automatically transforms Vala style lower_case_names to D-Bus style CamelCaseNames behind the scenes. You can use methods, signals, properties in D-Bus objects as if they were Vala objects.

Using GDBus
Server

/* Note: this attribute specifies the _interface_ name.  It
 * is called 'name =' for historical reasons.
 */
[DBus (name = "org.example.Demo")]
public class DemoServer : Object {
 
    private int counter;
 
    public int ping (string msg) {
        stdout.printf ("%s\n", msg);
        return counter++;
    }
 
    public int ping_with_signal (string msg) {
        stdout.printf ("%s\n", msg);
        pong(counter, msg);
        return counter++;
    }
 
    /* Including any parameter of type GLib.BusName won't be added to the
       interface and will return the dbus sender name (who is calling the method) */
    public int ping_with_sender (string msg, GLib.BusName sender) {
        stdout.printf ("%s, from: %s\n", msg, sender);
        return counter++;
    }
 
    public void ping_error () throws Error {
        throw new DemoError.SOME_ERROR ("There was an error!");
    }
 
    public signal void pong (int count, string msg);
}
 
[DBus (name = "org.example.DemoError")]
public errordomain DemoError
{
    SOME_ERROR
}
 
void on_bus_aquired (DBusConnection conn) {
    try {
        conn.register_object ("/org/example/demo", new DemoServer ());
    } catch (IOError e) {
        stderr.printf ("Could not register service\n");
    }
}
 
void main () {
    Bus.own_name (BusType.SESSION, "org.example.Demo", BusNameOwnerFlags.NONE,
                  on_bus_aquired,
                  () => {},
                  () => stderr.printf ("Could not aquire name\n"));
 
    new MainLoop ().run ();
}
valac --pkg gio-2.0 gdbus-demo-server.vala

Client
The methods of the client interface must be defined with throws IOError.

[DBus (name = "org.example.Demo")]
interface Demo : Object {
    public abstract int ping (string msg) throws IOError;
    public abstract int ping_with_sender (string msg) throws IOError;
    public abstract int ping_with_signal (string msg) throws IOError;
    public signal void pong (int count, string msg);
}
 
void main () {
    /* Needed only if your client is listening to signals; you can omit it otherwise */
    var loop = new MainLoop();
 
    /* Important: keep demo variable out of try/catch scope not lose signals! */
    Demo demo = null;
 
    try {
        demo = Bus.get_proxy_sync (BusType.SESSION, "org.example.Demo",
                                                    "/org/example/demo");
 
        /* Connecting to signal pong! */
        demo.pong.connect((c, m) => {
            stdout.printf ("Got pong %d for msg '%s'\n", c, m);
            loop.quit ();
        });
 
        int pong = demo.ping ("Hello from Vala");
        stdout.printf ("%d\n", pong);
 
        pong = demo.ping_with_sender ("Hello from Vala with sender");
        stdout.printf ("%d\n", pong);
 
        pong = demo.ping_with_signal ("Hello from Vala with signal");
        stdout.printf ("%d\n", pong);
 
    } catch (IOError e) {
        stderr.printf ("%s\n", e.message);
    }
    loop.run();
}
valac --pkg gio-2.0 gdbus-demo-client.vala

Type Table

D-Bus Vala Description Example
b bool Boolean
y uint8 Byte
i int Integer
u uint Unsigned Integer
n int16 16-bit Integer
q uint16 Unsigned 16-bit Integer
x int64 64-bit Integer
t uint64 Unsigned 64-bit Integer
d double Double
s string String
v GLib.Variant Variant
o GLib.ObjectPath Object Path
a [] Array ai maps to int[]
a{} GLib.HashTable<,> Dictionary a{sv} maps to HashTable<string, Variant>
() a struct type Struct a(ii) maps to Foo[] where Foo might be defined as
struct Foo { public int a; public int b };

Debugging D-Bus Applications
D-Feet
D-Feet is a graphical D-Bus debugger. This is what our little D-Bus service looks like in D-Feet:

dbus-monitor
Open a terminal and enter:

dbus-monitor

Excerpt from the output showing a property change notification:

signal sender=:1.454 -> dest=(null destination) serial=9 path=/org/example/demo; interface=org.freedesktop.DBus.Properties; member=PropertiesChanged
   string "org.example.Demo"
   array [
      dict entry(
         string "pubprop"
         variant             string "1018873421"
      )
   ]
   array [
   ]

Service with D-Bus property change notifications
This example will setup a D-Bus service that can send notifications on the change of properties. (example code partly by Faheem)

The timeout will change the property every few seconds. The notifications can be visualized by the terminal program ‘dbus-monitor’ that comes with most distributions.

[DBus (name = "org.example.Demo")]
public class DemoServer : Object {
 
    public string pubprop { owned get; set; }
 
    private weak DBusConnection conn;
 
    public DemoServer (DBusConnection conn) {
        this.conn = conn;
        this.notify.connect (send_property_change);
    }
 
    private void send_property_change (ParamSpec p) {
        var builder = new VariantBuilder (VariantType.ARRAY);
        var invalid_builder = new VariantBuilder (new VariantType ("as"));
 
        if (p.name == "pubprop") {
            Variant i = pubprop;
            builder.add ("{sv}", "pubprop", i);
        }
 
        try {
            conn.emit_signal (null, 
                              "/org/example/demo", 
                              "org.freedesktop.DBus.Properties", 
                              "PropertiesChanged", 
                              new Variant ("(sa{sv}as)", 
                                           "org.example.Demo", 
                                           builder, 
                                           invalid_builder)
                              );
        } catch (Error e) {
            stderr.printf ("%s\n", e.message);
        }
    }
}
 
public class NotificationsTest : Object {
 
    private DemoServer dserver;
 
    public NotificationsTest () {
        Bus.own_name (BusType.SESSION, "org.example.Demo", BusNameOwnerFlags.NONE,
                      on_bus_acquired, on_name_acquired, on_name_lost);
    }
 
    private void on_bus_acquired (DBusConnection conn) {
        print ("bus acquired\n");
        try {
            this.dserver = new DemoServer (conn);
            conn.register_object ("/org/example/demo", this.dserver);
        } catch (IOError e) {
            print ("%s\n", e.message);
        }
    }
 
    private void on_name_acquired () {
        print ("name acquired\n");
    }  
 
    private void on_name_lost () {
        print ("name_lost\n");
    }
 
    public void setup_timeout () {
        Timeout.add_seconds (4, () => {
            dserver.pubprop = Random.next_int ().to_string ();
            return true;
        });
    }
}
 
void main () {
    var nt = new NotificationsTest ();
    nt.setup_timeout ();
    new MainLoop ().run ();
}
valac --pkg gio-2.0 gdbus-change-notificationst.vala

Over!

From: https://live.gnome.org/Vala/DBusServerSample

GLib 创建自定义事件源

GLib 实现了一个功能强大的事件循环分发处理机制,被抽象成为 GMainLoop,用于循环处理事件源上的事件。每个 GMainLoop 都工作在指定的 GMainContext 上。事件源在 GLib 中则被抽象成了 GSource。在 GMainContext 中有一个 GSource 列表。GLib 内部定义实现了三种类型的事件源,分别是 Idle, Timeout 和 I/O。同时也支持创建自定义的事件源。

自定义事件源的基本作用
自定义的事件源可以用来将外部信号(事件)挂到程序中的指定主循环上,从而在 g_main_loop_run 中可以响应这些事件。

如何创建自定义事件源
GLib 提供了一系列的接口用于创建自定义的事件源,下面我们先讲解一下创建事件源的基本函数和数据结构,最后给出一些实例。

自定义的事件源是一个继承 GSource 的结构体,即自定义事件源的结构体 的第一个成员是 GSource 结构体, 其后便可放置程序所需数据, 例如:

typedef struct _MySource MySource;
 
struct _MySource
{
    GSource _source;
    gchar text[256];
}

实现了事件源数据结构的定义之后,还需要实现事件源所规定的接口,主要为 prepare, check, dispatch, finalize 等事件处理函数(回调函数),它们包含于 GSourceFuncs 结构体中。将 GSourceFuncs 结构以及事件源结构的存储空间宽度作为参数传给 g_source_new 便可构造一个新的事件源,继而可使用 g_source_attach 函数将新的事件源添加到主循环上下文中。下面这个示例可创建一个只会讲“Hello world!”的事件源,并将其添加到主事件循环默认的 GMainContext 中。

#include <glib.h>
#include <glib/gprintf.h>
 
typedef struct _MySource MySource;
struct _MySource
{
    GSource source;
    gchar text[256];
};
 
static gboolean prepare(GSource *source, gint *timeout)
{
    *timeout = 0;
 
    return TRUE;
}
 
static gboolean check(GSource *source)
{
    return TRUE;
}
 
static gboolean dispatch(GSource *source, GSourceFunc callback, gpointer user_data)
{
    MySource *mysource = (MySource *)source;
 
    g_print("%s\n", mysource->text);
 
    return TRUE;
}
 
int main(void)
{
    GMainLoop *loop = g_main_loop_new(NULL, TRUE);
    GMainContext *context = g_main_loop_get_context(loop);
    GSourceFuncs source_funcs = {prepare, check, dispatch, NULL};
    GSource *source = g_source_new(&source_funcs, sizeof(MySource));
 
    g_sprintf(((MySource *)source)->text, "Hello world!");
    g_source_attach(source, context);
    g_source_unref(source);
 
    g_main_loop_run(loop);
 
    g_main_context_unref(context);
    g_main_loop_unref(loop);
 
    return 0;
}

上述程序的 g_main_loop_run 函数运行时,会迭代访问 GMainContext 的事件源列表,步骤大致如下:
a. g_main_loop_run 通过调用事件源的 prepare 接口并判断其返回值以确定各事件源是否作好准备。如果各事件源的 prepare 接口的返回值为 TRUE,即表示该事件源已经作好准备,否则表示尚未做好准备。显然,上述程序所定义的事件源是已经作好了准备。
b. 若某事件源尚未作好准备 ,那么 g_main_loop 会在处理完那些已经准备好的事件后再次询问该事件源是否作好准备 ,这一过程是通过调用事件源的 check 接口而实现的,如果事件源依然未作好准备,即 check 接口的返回 FALSE,那么 g_main_loop_run 会让主事件循环进入睡眠状态。主事件循环的睡眠时间是步骤 a 中遍历时间源时所统计的最小时间间隔 ,例如在 prepare 接口中可以像下面这样设置时间间隔。到达一定时间后, g_main_loop_run 会唤醒主事件循环,再次询问。如此周而复始,直至事件源的 prepare 接口返回值为 TRUE。

static gboolean prepare(GSource *source, gint *timeout)
{
    *timeout = 1000; /* set time interval one second */
 
    return TRUE;
}

c. 若事件源 prepare 与 check 函数返回值均为 TRUE,则 g_main_loop_run 会调用事件源的 dispatch 接口,由该接口调用事件源的响应函数。事件源的响应函数是回调函数,可使用 g_source_set_callback 函数进行设定。在上例中, 我们没有为自定义的事件源提供响应函数。

上文自定义的事件源实际是 Idle 类型的,此类事件源,是指那些只有在主事件循环无其他事件源处理时才会被处理的事件源。GLib 提供了预定义的空闲事件源类型,其用法见下面的示例。

#include <glib.h>
 
static gboolean idle_func(gpointer data)
{
    g_print("%s\n", (gchar *)data);
 
    return TRUE;
}
 
int main(void)
{
    GMainLoop *loop = g_main_loop_new(NULL, TRUE);
    GMainContext *context = g_main_loop_get_context(loop);
 
    g_idle_add(idle_func, "Hello world!");
 
    g_main_loop_run(loop);
 
    g_main_context_unref(context);
    g_main_loop_unref(loop);
 
    return 0;
}

上述示例中,idle_func 是 idle 事件源的响应函数,如果该函数返回值为 TRUE,那么它会在主事件循环空闲时重复被执行;如果 idle_func 的返回值为 FALSE,那么该函数在执行一次后,便被主事件循环从事件源中移除。g_idle_add 函数内部定义了一个空闲事件源,并将用户定义的回调函数设为空闲事件源的响应函数, 然后将该事件源挂到主循环上下文。

Timeout 类事件源,GLib 也提供了预定义的定时器事件源,其用法与 GLib 预定义的空闲事件源类似。例如:

#include <glib.h>
 
static gboolean timeout_func(gpointer data)
{
    static guint i = 0;
 
    i += 2;
    g_print ("%d\n", i);
 
    return TRUE;
}
 
int main(void)
{
    GMainLoop *loop = g_main_loop_new(NULL, TRUE);
    GMainContext *context = g_main_loop_get_context(loop);
 
    g_timeout_add(2000, timeout_func, loop);
 
    g_main_loop_run(loop);
 
    g_main_context_unref(context);
    g_main_loop_unref(loop);
 
    return 0;
}

如果要自定义定时器类型的事件源,只需让事件源的 prepare 与 check 接口在时间超过所设定的时间间隔时返回 TRUE, 否则返回 FALSE。

I/O 类型的事件源要稍微难理解一些,因为涉及到了操作系统层面的 poll 机制。所谓 poll 机制,就是操作系统提供的对文件描述符所关联的 I/O 的状态监视功能 ,例如向文件中写入数据 ,那么 I/O 的状态可以表示为 POLLOUT, 而从文件中读取数据,那么 I/O 的状态就变为 POLLIN。GLib 为 Unix 系统与Windows 系统的 poll 机制进行了封装,并且可以将文件与主事件循环的事件源建立关联,在主循环的过程中, g_main_loop_run 会轮询各个关联到文件的事件源,并处理相应的事件响应。I/O 类型的事件源, prepare,其 check, dispatch 等接口的执行次序如下:

a. 主事件循环会首先调用 check 接口, 询问事件源是否准备好。因为此时, g_main_loop_run 尚未轮询那些与 I/O 相关联的事件源, 所以 I/O 类型的事件源, check 接口的返回值应该是 FALSE。其主事件循环调用 g_main_context_iteration 轮询各事件源,探寻是否有 I/O 类型事件源的状态发生变化,并记录变化结果。
b. 主循环调用 check 接口, 询问事件是否准备好。此时, 如果 I/O 类型事件源的状态变化符合要求,那么就返回 TRUEE,否则返回 FALSE。
c. 如果 prepare 与 check接口的返回值均为 TRUE, 那么此时主事件循环会调用 dispatch 接口分发消息。

下面的示例展示了一个自定义的 I/O 类型事件源的基本用法。该示例所产生的程序接受用户在终端中输入的字符串,并统计输入的字符数量。

#include <glib.h>
 
typedef struct _MySource MySource;
 
struct _MySource
{
    GSource _source;
    GIOChannel *channel;
    GPollFD fd;
};
 
static gboolean watch(GIOChannel *channel)
{
    gsize len = 0;
    gchar *buffer = NULL;
 
    g_io_channel_read_line(channel, &buffer, &len, NULL, NULL);
    if(len > 0)
      g_print("%d\n", len);
    g_free(buffer);
 
    return TRUE;
}
 
static gboolean prepare(GSource *source, gint *timeout)
{
    *timeout = -1;
    return FALSE;
}
 
static gboolean check(GSource *source)
{
    MySource *mysource = (MySource *)source;
 
    if(mysource->fd.revents != mysource->fd.events)
      return FALSE;
 
    return TRUE;
}
 
static gboolean dispatch(GSource *source, GSourceFunc callback, gpointer user_data)
{
    MySource *mysource = (MySource *)source;
 
    if(callback)
      callback(mysource->channel);
 
    return TRUE;
}
 
static void finalize(GSource *source)
{
    MySource *mysource = (MySource *)source;
 
    if(mysource->channel)
      g_io_channel_unref(mysource->channel);
}
 
int main(int argc, char* argv[])
{
    GMainLoop *loop = g_main_loop_new(NULL, FALSE);
    GSourceFuncs funcs = {prepare, check, dispatch, finalize};
    GSource *source = g_source_new(&funcs, sizeof(MySource));
    MySource *mysource = (MySource *)source;
 
    mysource->channel = g_io_channel_new_file("test", "r", NULL);
    mysource->fd.fd = g_io_channel_unix_get_fd(mysource->channel);
    mysource->fd.events = G_IO_IN;
    g_source_add_poll(source, &mysource->fd);
    g_source_set_callback(source, (GSourceFunc)watch, NULL, NULL);
    g_source_set_priority(source, G_PRIORITY_DEFAULT_IDLE);
    g_source_attach(source, NULL);
    g_source_unref(source);
 
    g_main_loop_run(loop);
 
    g_main_context_unref(g_main_loop_get_context(loop));
    g_main_loop_unref(loop);
 
    return 0;
}

像 Idle 类型与 Timeout 类型事件源那样,GLib 也提供了预定义的 I/O 类型事件源,使用它可以将上例简化为:

#include <glib.h>
 
gboolean io_watch(GIOChannel *channel, GIOCondition condition, gpointer data)
{
    gsize len = 0;
    gchar *buffer = NULL;
 
    g_io_channel_read_line(channel, &buffer, &len, NULL, NULL);
    if(len > 0)
      g_print("%d\n", len);
    g_free(buffer);
 
    return TRUE;
}
 
int main(int argc, char* argv[])
{
    GMainLoop *loop = g_main_loop_new(NULL, FALSE);
    GIOChannel* channel = g_io_channel_unix_new(1);
 
    if(channel)
    {
        g_io_add_watch(channel, G_IO_IN, io_watch, NULL);
        g_io_channel_unref(channel);
    }
 
    g_main_loop_run(loop);
 
    g_main_context_unref(g_main_loop_get_context(loop));
    g_main_loop_unref(loop);
 
    return 0;
}

Over!