Using SCTP in GLib

A while ago, I wrote about using a UDP socket as event source in GLib. Now, I’m migrating my program to SCTP. The main reason is that SCTP provides reliability for sequential messages, but some of the advanced features might become useful later. Surprisingly few changes were necessary in my program:

The relevant code parts now look like this:

GSocket *sock;
GError *err = NULL;
sock = g_socket_new(G_SOCKET_FAMILY_IPV6,
                    G_SOCKET_TYPE_SEQPACKET,
                    G_SOCKET_PROTOCOL_SCTP,
                    &err);
g_assert(err == NULL);

g_socket_bind(sock,
              G_SOCKET_ADDRESS(addr),
              TRUE,
              &err);
g_assert(err == NULL);

int fd = g_socket_get_fd(sock);
GIOChannel* channel = g_io_channel_unix_new(fd);
guint source = g_io_add_watch(channel, G_IO_IN,
                              (GIOFunc) callback_function, data);
g_io_channel_unref(channel);

g_socket_listen(sock, &err);
g_assert(err == NULL);

Sadly, it seems like there are no functions for GSocket to use lower level SCTP features, like getting the list of peer and local IP addresses in the association. I suppose this is possible by using the normal low level functions (e.g. sctp_getpaddrs()) on the file descriptor returned by g_socket_get_fd(), but I haven’t tried yet.

Get USB device information from sysfs

The Linux kernel names USB devices depending on their location on the bus. When I plug my USB flash drive into my computer, a line like this will appear in the kernel log:

usb 1-4: new high speed USB device using ehci_hcd and address 7

“1-4” is the kernel’s name for the device: “1” is the root hub number, “4” the number of the hub port. With this information, it is easy to find the device in sysfs:

/sys/bus/usb/devices/1-4/

Using files in this directory, I can check what kind of device it is or how much power it requests from the bus.

$ cat /sys/bus/usb/devices/1-4/product
Mass Storage Device
$ cat /sys/bus/usb/devices/1-4/bMaxPower
100mA

There’s a lot more, including the device’s vendor ID, product ID and serial number. You might be wondering “Why not just use lsusb?” Most of the time, you can do that. In cases were you can’t, like an embedded system without usbutils, knowing the sysfs path can be really useful. I actually needed this today, and it’s interesting anyways. πŸ˜‰

If you want to know more about this topic, I would recommend reading “Linux Device Drivers“, chapter 13 (the sysfs part starts at page 333).

Trying out pam_namespace

I stumbled upon this post, and I remembered that I wanted to try using pam_namespace since I read about it on Flameeyes’ blog. So today, I actually did.

What is polyinstantiation?

Most people would expect that two users reading from the same absolute path in the file system tree would get either the same file or an “access denied.” But it does not have to be like this: Linux’s mount namespaces allow you to have many different file system trees. This can be used to create multiple (“poly“) instances of system directories like /tmp/ for different users. This is done by bind-mounting (look at mount’s --bind option) different directories to the system directory in a user-specific namespace. The advantage: a user cannot access other users’ files, which protects against certain types of security problems. pam_namespace.so is used to do this automatically at login.

Example

There are two users in the system (Alice and Bob). Due to separate namespaces, Bob will see the contents of /tmp-inst/bob/ when looking into /tmp/, while Alice would see the contents of /tmp-inst/alice/. If Alice writes private data to /tmp/secret (bad idea, just an example) and forgets to restrict access rights, Bob won’t be able to see or even read it. As seen from Bob’s namespace, the file is in /tmp-inst/alice/secret, and he has no access rights there.

Prerequisites

Gentoo’s default PAM installation includes the pam_namespace.so module. The configuration consists of two parts:

  1. Configure pam_namespace (/etc/security/namespace.conf).
  2. Change the PAM configuration to use the namespace module.

Make sure you know what you’re doing before changing the PAM configuration (see “The Linux-PAM System Administrators’ Guide“)! If you break your login badly enough, you might have to use a live-system (from USB-disk, CD or whatever) to repair it. I always keep a root login open while changing the PAM configuration, so that I can undo my changes if something goes wrong. Concerning the namespace module, reading “Improve security with polyinstantiation” was a good start, but I’d recommend reading the official documentation as well.

Namespace setup

I want to polyinstantiate /tmp/. The user specific instances will be kept in /tmp-inst/, so my /etc/security/namespace.conf looks like this:

/tmp    /tmp-inst/              user    root,adm

user” is the “polyinstantiation method” and will cause polyinstantiation based on user name. If you use SELinux, some other methods are available. The “root,adm” in the fourth column says that these users should not get polyinstantiated directories. The parent directory for the user-specific instances must exist and have access rights set to 000. To create it like in this example:

# mkdir /tmp-inst/
# chmod 000 /tmp-inst/

The parent directory could have another name or be kept inside the polyinstantiated directory.

PAM config

pam_namespace.so provides only the session type. To use it, you’ll have to add a line similar to

session         required        pam_namespace.so

to the right configuration file. Which file that is depends on your distribution and use case. For me, it is /etc/pam.d/system-auth. However, with a line exactly like that, I found one problem: Console login as root, everything as expected (the “real” /tmp/). Login as user worked as well (instance directory). The problem was that the instance directory remained mounted when I used “su -” from a running user session to become root. I found the solution in the module documentation: The option unmnt_remnt makes it unmount possibly existing namespaces before setting them up for the new session:

session         required        pam_namespace.so unmnt_remnt

With this configuration, I get the unmodified file system tree after “su -“. πŸ™‚ Hint: The options “debug” and “ignore_config_error” might be useful for testing. Have fun!

Events and threads: A mutex is not enough!

I wrote a little UDP server that would process messages like this:

  1. Incoming data on the UDP socket creates an event in the GLib main loop.
  2. The event callback function starts a handler thread from a GThreadPool.
  3. The thread locks a mutex for the socket, reads from the socket, maybe sends a reply (depends on the received message), unlocks the mutex and returns.

Looks harmless? It isn’t. The program used a signal callback to stop the main loop on SIGTERM. This would allow any remaining threads to finish their work before the program terminates. When I tested that, I was surprised that the server would not stop. I had to add a lot of debug output until I found the reason.

The problem was that the messages were read from the socket in the threads. The event callback returned after starting the thread, and the main loop continued to run. In the short time between thread start and socket read, the event would fire again and again, because the data was still available on the socket. This was enough to launch all handler threads in my 4 member thread pool.

The result: The first thread gets the mutex, reads the message, does its work, unlocks the mutex and returns. While the first thread reads, there are 3 more threads waiting for the mutex. When the first thread is done, one of the others grabs the mutex and tries to read. I used blocking functions, so it waits until a new message arrives. A new message fires an event, which starts another thread. I suppose you get the idea. All handler threads were trying to process incoming messages simultaneously! By the way, the SIGTERM callback actually worked. After SIGTERM, no new events and therefore no handler threads were created, and after receiving enough messages the server did indeed terminate, because each thread returned after processing its message.

This is why I wrote that a mutex is not enough. Yes, the mutex prevented the threads reading from the socket at the same time, but it did not stop the event. The solution was, of course, to read the message in the event callback and point the handler thread at the data. Lesson learned: Don’t just think about “Could these threads try to access something at the same time?” and throw in a mutex if the answer is yes. Threaded operation can create a lot of unintended connections, and it’s easy to overlook one of them. Take care, and you can avoid a long debugging session like the one I had today. πŸ˜‰

Returning a string array from a D-Bus method call

I am writing a D-Bus service using dbus-glib, and I want one of my methods to return a variable number of strings. Sounds easy enough, right? Arrays of strings are listed in the D-Bus tutorial with the type signatureas“. I was still unsure of the right C data type for the method signature, and surprisingly I could not find a single example where this was actually used as a return value. The only hint I had was the following information from the tutorial:

  • Type signature: as
  • GType: G_TYPE_STRV
  • C typedef: char **
  • Free function: g_strfreev

What is a G_TYPE_STRV? The documentation describes it as a “NULL-terminated array of strings.” This means that it is an array of gchar *, and the last pointer in the array has to be NULL, so that you can easily recognize the end of the array. Such an array is technically a gchar **, which matches the type definition cited above. Therefore, my handler function will take a gchar **return_string_array as a parameter and set that pointer to point at the returned array.

The difficulties started when I tried to actually fill the array. An empty array

*return_string_array = NULL;

worked, but I suffered a lot of segfaults trying to put useful data into it. This is the solution I found (GStrv is just a typedef for the kind of string array described above):

/* Inside the message handler function that gets
 * "gchar **return_string_array" as a parameter */
GStrv s = g_new(char *, 3);
s[0] = g_strdup("Hi!");
s[1] = g_strdup("This is a test.");
s[2] = NULL;
*return_string_array = (gchar *) s;

Step-by-step description

  1. Allocate memory for the required number of pointers (number of strings plus one for the NULL pointer).
  2. Fill the strings (g_strdup is a nice little function to put strings on the heap), don’t forget the NULL at the end.
  3. Have the return pointer point at the data you placed on the heap.

The last line of code is not very intuitive (at least for me), but it makes sense: For each outgoing argument the function gets a pointer to a memory area it is supposed to fill with the returned data. The reason for this is that D-Bus method calls can return multiple values, while C functions can not. So the C function returns a gboolean which is TRUE in case of success and FALSE in case of error. The return data is passed by writing to memory provided by the caller. In this case, the returned data is an array of pointers to strings, so the provided memory is a pointer which must point at the first element of the array (which is another pointer). Quite a bit of pointer confusion, but it works. πŸ™‚

A visit to “Café Noir”

Yesterday, a group of students (most, but not all of them visually impaired) held a special event at my university: “Café Noir.” The idea is simple: Have a café in a pitch-dark room (“noir” is French for “black”). This way people with normal sight can experience what it is like to be unable to see in a way that’s actually fun. I knew that two friends of mine were part of the staff (one of them told me about the event) and it sounded interesting, so I decided to go. You had to get reservations, which were sold the week before. I got the last but one ticket – just in time!

My reservation was for 5 pm, so after work I went to the room stated on the ticket instead of going home. When I arrived, a bunch of people were already waiting outside the room. After a few minutes, the organizer came out. She asked us to form groups of six and line up so that everyone would touch the shoulders of the person in front of them. The groups would go in one after the other. I didn’t know any of the other guests, so I joined a random group. When it was my groups’ turn, she led us into a small room I call a “lightlock”: It’s like an airlock, but to keep light out rather than air in. After the outer door was closed, the room was dark. The inner door was opened and we were introduced to our waitress, who I’m going to call Anna (not her real name – I forgot to ask if I could use it). She brought us into the café. More precisely: She guided the first person in the line, and the others followed. Inside, she told us to wait and took us to our seats one after another.

Having tea without vision

While moving into the dark, my movements became slower and more careful. Anna told us that we would find a napkin and a spoon in front of us (I found that quickly) and sugar, milk and an empty bowl for waste in the middle of the table. Feeling around on the table, I found the milk and empty bowl. The sugar must have been further away, but I found a can of whipped cream. We were asked about drinks, and I chose tea. I first got a cup, then the teapot.

Now you might wonder how to pour in tea without seeing anything. Anna explained it, and actually it is not too difficult: Put your finger into the cup so that the finger tip is approximately on the level until which you want to pour your drink, then pour carefully and stop as soon as the liquid touches your finger. The hot tea was a bit tricky because the inside of the cup got warm and humid pretty quickly, and I wondered if the water I felt was tea or just condensation. We had no major spills at out table, though. Once the tea was in the cup and the cup in my hand, drinking was almost as easy as with light.

At about the same time when we got our drinks, the organizer announced a piano player. This was a bit funny, because as it turns out I know him as well (although I might have guessed he’d be involved). From this point on, we had really nice music in the background. πŸ™‚

Eating in the dark was a bit more tricky than drinking. I had ordered fruitcake. The first challenge was to find the cake on the plate. Tea will flow the right way if you raise the cup to your lips, but cake won’t get to the spoon by itself. I ended up using my fingers a lot: Find the cake, use the spoon to split off a piece, use the fingers to make sure it stays on the spoon on the way to my mouth. I was extra careful after putting cream onto my cake. πŸ˜‰

What’s in this cake?

While eating, we talked a little, mainly about things we noticed about the current situation, like what was easy and what difficult to do in the dark, what kind of fruit was in the cake and so on. It’s surprising how long it took to figure out the kind of fruit in the cake. Of course I know how different kinds of fruit taste, but without sight to confirm it took a few tries to be sure (it was peach and cherry).

A funny sentence overheard from a neighboring table: “Now we can’t see if Sue [name changed] blushes!” I don’t know what the conversation was about and if the previous remark was just sweet, a huge compliment or whatever, but yes, facial expressions don’t work if you can’t see. One of the things we use daily without thinking about it.

Paying was easier than you might think: Euro coins have notches on their side that are specifically designed to tell them apart by touch. I admit that I practiced that before. πŸ˜‰ A guy at my table paid with a 5€ bank note, which made the girl next to him ask how he recognized that in the dark. With a grin I could not see but hear, he replied: “Well, I know that I have just two 5€ bills in my wallet.”

My feeling for time was completely off. When it was time to leave, I wouldn’t have guessed that almost one hour had passed. I don’t think that was due to the dark itself, rather the sheer amount of unusual impressions. Anna asked us to stand up and helped us to form a line again and led us to the “lightlock”, where we said goodbye.

Outside, I had to wait a moment until my eyes got used to the sudden brightness. On the way home I had a lot to think about. I never really thought about how much I use my eyes, I just do. A very nice and interesting afternoon. A huge thanks to everyone who helped making this possible!

Using a UDP socket as event source in GLib main loop

I’ve been rewriting a small UDP server using GLib. I found it difficult to get started with GLib, but well worth it – the code is a lot cleaner now. One thing that took me particularly long to figure out is how to integrate listening on a UDP socket into the GLib main loop. It’s not really complicated, but the parts are scattered in different parts of the GLib and GIO API documentation.

Obviously, a GSocket is necessary. I want my socket to listen on IPv6 UDP. I know that assertions are not the best error handling, but they are good enough for an example. πŸ˜‰

GSocket *sock;
GError *err = NULL;
sock = g_socket_new(G_SOCKET_FAMILY_IPV6,
                    G_SOCKET_TYPE_DATAGRAM,
                    G_SOCKET_PROTOCOL_UDP,
                    &err);
g_assert(err == NULL);

Bind the socket (addr is the GInetSocketAddress* to listen on):

g_socket_bind(sock,
              G_SOCKET_ADDRESS(addr),
              TRUE,
              &err);
g_assert(err == NULL);

Hint: if you want to listen on all interfaces, use g_inet_address_new_any() to create the GInetAddress part of the socket’s address. Under Linux “any IPv6 address” includes IPv4, I don’t know if this applies on other platforms as well.

To get an event when the socket receives data, the following steps are necessary:

  1. Get a file descriptor for the socket.
  2. Use the file descriptor to create a GIOChannel for the socket.
  3. Add a watch for the G_IO_IN condition on the channel.
int fd = g_socket_get_fd(sock);
GIOChannel* channel = g_io_channel_unix_new(fd);
guint source = g_io_add_watch(channel, G_IO_IN,
                              (GIOFunc) callback_function, data);
g_io_channel_unref(channel);

The callback_function has to match the GIOFunc pattern. It must not block, so I use it to start a thread which will do the real work. The main loop needs to be initialized before attaching the event. After the event is attached, just run the main loop (if you need more info about that, look here). Cleaning up is pretty simple: Use g_source_remove(source) to disconnect the event source and g_object_unref() the objects.

Learning GObject basics

GLib and GObject enable object oriented programming in pure C. I will need a GObject later to build a D-Bus interface, so I wanted to write a simple class to learn how to use it. After reading “Introduction to GObject” by Amos Brocco and the tutorial part of the GObject Reference Manual, I started writing my own class. I could take a large part of the basic code from the examples in the reference manual, but a few things were somewhat confusing:

  • Which functions do I have to implement?
  • Where should I define what struct?
  • Do I need to allocate the private memory?
  • Should the object struct contain a pointer to the private memory?

Here are the answers that I found:

Necessary functions

Four functions are the absolute minimum: init, class_init, dispose and finalize. For a class named example, you would need the following declarations in the header:

static void example_init(Example *self);
static void example_class_init(ExampleClass *klass);
static void example_dispose(GObject *gobject);
static void example_finalize(GObject *gobject);

The argument to class_init should be called klass to avoid confusion with the C++ class keyword.

Structs

The object und class structs have to be declared and defined in the header (example). You can put the private data struct declaration into the header or source, the definition should definitely be in the source file. I would recommend putting the typedef into the header, because it will allow you to add a pointer to the private memory into the object struct. Put something like this into the header:

typedef struct _Example Example;
typedef struct _ExampleClass ExampleClass;
typedef struct _ExamplePrivate ExamplePrivate;

struct _Example {
	GObject parent_instance;
	/* instance members */
	ExamplePrivate *priv;
};

struct _ExampleClass {
	GObjectClass parent_class;
	/* class members */
};

The definition of struct _ExamplePrivate goes into the source file. The contents of the private structure is entirely your decision.

Allocating and accessing private memory

Register the private memory with the object system using g_type_class_add_private() in your class’ class_init() function. It will be allocated automatically when you use G_TYPE_INSTANCE_GET_PRIVATE for the first time, and freed automatically as well. However, you have to take care of any dynamically allocated memory in the private data. To quote the manual:

“You don’t need to free or allocate the private structure, only the objects or pointers that it may contain.”

If you use the source layout recommended in the manual, you will get a useful macro called something like EXAMPLE_GET_PRIVATE which returns a pointer to the private memory. Using a *priv pointer is not necessary (you could use GET_PRIVATE each time you need it), but it is a good idea to improve performance (the macro does a few function calls).

You can see how to register and get private memory in the example source file, look at the maman_bar_class_init() and maman_bar_init() functions and the MAMAN_BAR_GET_PRIVATE macro.

Catch SIGTERM, exit gracefully

I knew that programs can catch a SIGTERM and exit gracefully. What I didn’t know is how to do that, and that it’s actually quite simple. You need just two things:

  1. A function that will cause your program to exit gracefully
  2. The sigaction() function and the struct of the same name, defined in the system header signal.h.

Example

#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

volatile sig_atomic_t done = 0;

void term(int signum)
{
	done = 1;
}

int main(int argc, char *argv[])
{
	struct sigaction action;
	memset(&action, 0, sizeof(struct sigaction));
	action.sa_handler = term;
	sigaction(SIGTERM, &action, NULL);

	int loop = 0;
	while (!done)
	{
		int t = sleep(3);
		/* sleep returns the number of seconds left if
		 * interrupted */
		while (t > 0)
		{
			printf("Loop run was interrupted with %d "
			       "sec to go, finishing...\n", t);
			t = sleep(t);
		}
		printf("Finished loop run %d.\n", loop++);
	}

	printf("done.\n");
	return 0;
}

How does it work?

sigaction(SIGTERM, &action, NULL) sets the action to be performed for the SIGTERM signal to the term() function. This means that term() will be called when the program receives a SIGTERM. It will set the global variable done to 1, and that will cause the loop to stop after finishing the current run. That’s it!

Hints

  1. sig_atomic_t is a special type that guarantees that reads and writes are atomic, so the assignment to done cannot be interrupted, e.g. if another signal arrives while the handler is running. If you use glibc, it’s probably identical to int.
  2. done is declared volatile to let the compiler know it might change asynchronously. Otherwise an optimizing compiler may assume that since done does not change inside the loop the check can be omitted, creating an endless loop.
  3. Extending the point above, a signal handler function can be called at unpredictable times and must not mess up shared data structures. This restricts what kinds of calls you can safely make inside a handler. The signal(7) manpage has a list of Async-signal-safe functions.
  4. I wrote this example in such a way that term() will be called only for SIGTERM, but in a real program you should check the value of signum.
  5. You should also check the return value of sigaction() to make sure the call was successful.
  6. The third argument to sigaction() could be a struct sigaction* to store the previously set action.

Of course, you can add handlers for other signals the same way. Take a look at the sigaction(2) manpage! struct sigaction also has some more options to configure signal handling.

Running the example

I compiled the example into sigterm-example. Starting it and sending a SIGTERM by using

$ kill PID

(replace PID with the actual process ID) results in the following output:

$ ./sigterm-example
Finished loop run 0.
Finished loop run 1.
Finished loop run 2.
Loop run was interrupted with 2s to go, finishing...
Finished loop run 3.
Done.

Of course, the “Loop run was interrupted […]” message will only be printed if the SIGTERM actually hits during sleep().

To show the difference between SIGTERM and SIGKILL (which cannot be caught), another run with kill -SIGKILL PID:

$ ./sigterm-example
Finished loop run 0.
Finished loop run 1.
Finished loop run 2.
Killed

You can see that SIGTERM lets the program finish its work, while SIGKILL forces it to terminate immediately. Try adding a handler for SIGKILL if you don’t believe me! πŸ˜› If a bug in your SIGTERM handling makes the program refuse to stop, you’ll know what to do. πŸ˜‰

This post has been updated on 2013-05-23 after I noticed that the signal(2) manpage discourages the use of signal() in favor of sigaction().

Update 2014-10-02: Declare done as volatile sig_atomic_t, unsafe printf() call has been removed from the example handler and a comment about async signal safety has been added (Thanks, Anders!).

Neelie Kroes on Open Standards

Here’s my opinion on a speech by Neelie Kroes (EU Commissioner for Digital Agenda) about “How to get more interoperability in Europe”, held on 2010-06-10. You can find the full text here.

“I will start with a confession: I am still a big fan of open standards. I believe in openness, and I believe in practising what one preaches.”

Sound’s good, doesn’t say much, though. πŸ˜‰

“Some observers think “open standards” is a tainted term that should not to be used in the absence of a generally recognised definition. Others act as if a policy document that does not mention “open standards” would automatically lack merit. My position is in between: I don’t believe that listing keywords can substitute for policy. Whatever the labels, what matters is the substance. I would urge all stakeholders to focus on the content of the package rather than the wrapping.”

I can’t say I disagree about content being more important than the label. However, it is well-known that terms can be and have been chosen or misinterpreted intentionally to suit someone’s interests. Yes, we should avoid unnecessary hassle, but still be careful about this.

The first parts of the speech are mostly about why open standards are useful: better standards, better implementation, more competition, avoiding vendor lock-ins, etc., and all of that saves money. This is mixed with some general political ideas about how to promote open standards. Nothing too interesting, until the end of the “avoiding vendor lock-ins”-part:

“It is even worse when such decisions result in the waste of private money on top. That happens where the public authorities’ decisions force citizens to buy specific products (rather than any product compliant with an applicable standard) in order to make use of a public service. This could be your kid’s school insisting on the use of a specific word processing system or your tax department’s online forms requiring a specific web browser.”

Finally! Situations like this have annoyed me for a long time. I hope Ms. Kroes will not stop at recognizing the problem and take steps against it. After this, she talks about the proposed new version of the “European Interoperability Framework”:

“For me, it is a fundamental tenet that public administrations spending tax-payers’ money should opt for the least constraining solution that meets the requirements for a given need. Such a rule, as the default, would shield public authorities from the dangers of long-term lock-in. It would also ensure competition between suppliers for follow-up contracts and for services. Opting for closed solutions would be possible, but on the basis of a clear justification, rather than because it was the easy option.”

That would definitely be a step into the right direction, and it would help solving the problem of forcing citizens to use a certain product as mentioned before. However, I would like to see an additional rule that requires the development of an open replacement if a closed solution was chosen (in case of software: a Free Software solution). The last part of the speech is about how “to ensure that significant market players cannot just choose to deny interoperability with their product.”

“The Commission should not need to run an epic antitrust case every time software lacks interoperability. Wouldn’t it be nice to solve all such problems in one go?

Therefore I am looking for a way to ensure companies offer the required information for licensing. We are thinking very hard about how this could be achieved. Any such initiative would probably be limited to certain types of IT products. And it would likely involve some form of pricing constraints.

[…] Just to be clear, while it is still early days, it is certainly possible that I will go for a legislative proposal.”

Now this is really interesting. Does that mean that we might get a law that forces software vendors (or other parties involved) to release documentation of their file formats, protocols or similar things? “Pricing constraints” sounds like demanding royalties would still be allowed, but at least no arbitrary amounts. While I don’t think having to pay for access to a standard definition is right, this would avoid lock-ins anyway. Well, at least as long as there are no other restrictions involved – the word “patent” is not mentioned in the speech.

My proposals

  1. Definition of Open Standards: Choose the W3C definition! The W3C’s process is proven in practice to provide high quality and widely adopted standards. This is what people expect when they hear the term “open standard”. And it is the right thing to do anyway – knowledge must be free!
  2. Public agencies should use only standards compliant solutions, no loopholes! This avoids lock-ins for the them and trouble for the public (see above). I’d like to see a preference for Free Software, especially in education.
  3. Forcing vendors to release file formats and similar documentation is also a matter of consumer protection, because consumers can become victims of lock-in, too. Royalties should not be necessary. The previous proposal might solve most of this due to the important role of public spending in the market.
  4. Stop software patents! Free access to a standard definition of limited use if it may not be implemented.

Conclusion

Ms. Kroes’ speech ended:

“We can’t change all of this in one year, and there will be plenty who try to stop change. But I still want that change, and I will keep coming back and speaking to you until we achieve it.”

If she can make this happen, it will be a huge step into the right direction. Not as far as I’d like, but hey, it’s a start! πŸ™‚ My ideas for further improvement are mentioned above. What do you think?

Design a site like this with WordPress.com
Get started