Monthly Archives: September 2006

The Unix way in Windows, msmtp(1) and ssh example

The task. There is Nagios system running on some host in LAN. It checketh for some things and if something is bad, it sendeth out notifications to people who should fix it. The problem is that there is no Internet access from the LAN, so it is impossible to send out notifications using Internet e-mail, only locally. There is, however, an SMTP server in the LAN, which provideth access to Internet e-mail, but the host where Nagios is running is not allowed to send e-mail. The task is to solve this problem somehow.

Now there is true Unix solution: take some host in the LAN which is authorized to send mail and make Nagios remotely call something on it to send mail, like this:

echo “Notification” | ssh allowed-host nail -s “Subject” to@address.zone

Where nail on allowed-host is configured to send mail using msmtp(1).

However, on all authorized hosts there are only Windows and ancient HP-UX systems. HP-UX is a problem because everything so ancient there so it is virtually impossible to install msmtp(1) and nail(1) there. But who said that the Unix way solution cannot be applied to Windows? Nobody. So…

Final design of the solution looketh like this:

echo “Notification” | nail -A “Inet” -s “Subject”

Where “Inet” nail account is configured to use something like this instead of sendmail:

ssh allowed-host msmtp “$@”

Now the only problem is to install msmtp on the Windows machine and set up ssh access to it. msmtp was not a problem at all–binary distribution for Windows is available. As for ssh, Cygwin did its job. OpenSSH Cygwin package containeth very nice command ssh-config-host-or-something which configureth ssh to run as NT service. The funny part was that Cygwin must be downloaded from Internet using setup.exe, which is Windows application. But, as I said, there is no Internet access there and I have no Windows at home. So I had to run setup.exe using wine, and it worked nice except for that I had to switch wine to virtual desktop mode.

Now everything is set up and working just fine. Everything took about two or three hours, mostly because I never did anything like this before. I can not imagine more elegant solution.

Programming language rating criteria

When thou hearest or readest “programming language X is better than Y”, or “programming language X is good, and Y is bad” or even “programming language X is the best”, believe it not.

The point is that, when one rateth something, he must choose some of criteria first. It is not that easy for programming language. Let us try to define some of them:

– How easy is it to learn?
– How long taketh it to implement a specific task in it? (Note there is a lot of tasks, so it is not a single criterion.)
– How fast will this implementation run? (Again, a lot of criteria.)
– How portable is it? (Both at the source and binary levels.)
– What kind of tools provideth it? (There is a lot of criteria again, because “tools” may be: libraries for a lot of tasks, code generators, development technologies, etc.)
– How transparent is its syntax?
– …

Now, when thou takest a real example, first thing thou shouldst do is to learn a lot about these languages, and then came up with criteria to compare them. It should be then obvious what is better and when.

Example: Delphi and C++. Delphi is easy to understand by idiots (+), not portable (-), compileth fast (+), hath a lot of long keywords (-), programs written in it execute at native speed (+), hath no good tools and libraries (-), hath averagely complicated syntax (0). Now, C++ is not that easy to understand by idiots (-), very portable (+), compileth slowly (-), programs written in it execute at native speed (+), hath a lot of good tools and libraries (+, including those for C), hath averagely complicated syntax (0). So, if thou art not considering thyself an idiot, not obsessed with fast compilation and canst live without a lot of keywords (compare “function vasia: int” with “int vasia()”), it should be obvious then that C++ is better without doubt. The main reason for Delphi existence is that it is easy to understand by idiots, yes.

Of course it all may seem a little bit prejudiced. For example, I wrote “hath averagely complicated syntax” for both of languages without any reasoning or explanation. Many would say, “Hey, Delphi is far more better to understand than those ‘while ((*s1++ = *s2++) != ‘\0′);’!”. Well, that meaneth not that what I have said is nonsense, but simply that thou hast to define criteria for thyself. But in order to do that, thou hast to learn both languages rather than looking at the sources and saying, “Oh, I don’t like this *s1++”. That is, of course, if thou ever considerest to compare these specific languages by this specific criterion – in my opinion, the very fact that Delphi is importable should be a good enough reason to throw it away without any second thought.

But the point is not that Delphi is far more worse than C++ (although I believe it is so), but that in order to compare something, criteria must be chosen and subjects must be thoroughly studied based on these criteria. Otherwise, it is just meaningless.

Программирование в России

Цитата с RSDN.RU:

“Сейчас в конференциях можно часто слышать, что ‘Кто не знает английского, тот не программист’. Понимая чудовищную несправедливость такого положения вещей, мы и решили создать RSDN.”

И добавлю от себя: но это их не спасёт! Потому что тот, кто не знает английского – действительно не программист и быть им не может. Мне это давно стало очевидно, и причина тоже понятна: передовые технологии имеют мировой масштаб, стало быть и документация к ним составляется на мировом, то есть английском, языке. Перевод на русский язык всегда отстаёт и в большинстве случаев ещё и вносит искажения. Стало быть, не зная английского языка, можно быть только отсталым программистам. И сколько бы там информации на RSDN не собирали – это всегда будет прошлый век. Впрочем, там и информация ещё та в плане компетентности, что тоже вполне логично – раз грамотные люди, понимая положение вещей, смываются на англоязычные ресурсы, остаётся только “by lamers for lamers”.

Для подтверждения ищем в блогояндексе “программирование”, а в блогогугле – “programming”. Сравниваем результаты.

На русском – реклама, рассуждения идиотов и сатанистов о .Net, анекдоты, и огромное количество ламерства, когда одни ламера пытаются убедить других ламеров, что вот C++ и Delphi – это круто, а Java, к примеру, отстой. Вместо “C++”, “Delphi” и “Java” можно подставить всё, что угодно: поскольку логики в их рассуждениях никакой, смысл от этого не изменитсяпоявится. Больше всего меня поражает именно ламерство – давно я заметил, что в нашей стране принято иметь завышенное самомнение. Человек пишет “Я считаю себя компетентным в вопросе языков программирования, потому как написал компилятор для, придуманного мною, языка” (пунктуация автора сохранена). С таким же успехом можно сказать “Я считаю себя экспертом по транспортным средствам, так как собрал своими руками велосипед”. И этот эксперт утверждает, что “С++ и С – это одно и то же”, уточняя: “С++ = С + syntax sugar + препроцессор для эмуляции объектно-ориентированного языка программирования”. Ага, компьютер – это калькулятор плюс мышь, плюс эмулятор искуственного интеллекта. Всё это почему-то идёт под заголовком “Язык для работы с железом: C, C++, ассемблеры, языки для написания шэйдеров т.п.”, чтобы совсем уже как у Груши было. И вот на таких вот экспертах и держатся российские ресурсы по программированию. Нет, конечно, среди кучи говна можно и что-нибудь полезное раскопать, но человек, копающийся в говне, когда рядом есть огромное количество уже готовой к употреблению продукции, вряд ли вызовет уважение у своих коллег.

В контрасте, на английском языке, первая же страница поиска содержит: ссылки на различного рода спецификации, официальную документацию (чего на русском нет, так как она пишется на английском языке, а ссылки на англоязычные ресурсы у нас ставить не принято), обсуждения таких вещей, как Java 2 EE, функционального программирования, wxWidgets, стили кодирования (этого на русском мне тоже почему-то не попалось). Попадаются и статьи про питон, о котором в русских источниках мне попалось только “говорят, что язык хороший, не знаю, не пробовал”. Конечно, дерьма и на английском хватает, но соотношение совсем другое.

В общем, независимо от самомнения представителей отдельных наций, люди, не знающие английского, будь они русскими, французами, немцами, китайцами или зулусами, не смогут стать профессиональными программистами.

Task, design and implementation

In order to solve almost any problem, it is very important to understand three concepts: task, design and implementation. Well, maybe these words sometimes fit not well enough to represent what I mean here, but nonetheless, there are three important concepts to understand while trying to accomplish almost anything, especially if it is related to software development. But it also aplieth not only to software development.

Basically, task is what needeth to be done, design is how it is done in terms of external appearance, and implementation is how it is done internally.

Let us take an example. Suppose I have to write a calculator that can perform four basic arithmetic operations on floating point numbers. This is the task. Given the task, I need to design and implement it. Design issues would be: what user interface a program should have, on which platforms it should run on, how fast it should run on a particular piece of hardware, et cetera. Implementation issues depend on design ones. For example, if I want to design it so it will perform X operation N times per second (assumed that it is possible at all), I must think about how to achieve it–and this is implementation issue. If I want it to have command line interface, then I have to think about command line options. If I want it to have GUI, then I must think what GUI should look like, what toolkits to use, et cetera.

Note that I put execution speed requirement in the design, and it is not mistake because it affecteth the way how program can be used. If it is fast enough for manual use by human, it may be too slow to use it as a part of some script that performs some complicated scientific calculations. In this case, we shall say: it is not designed to be used in such way. Do not forget about premature optimisation, though: it is actually a type of mistake where thou triest to implement something that thou designed not, that is, thou triest to raise execution speed without any particular goal in mind, just “to be faster”. It is bad, very bad. Read The Art of Unix Programming by Eric S. Raymond.

One important thing about all this stuff is that it is flexible. Implementation is the most flexible part–it is usually entirely up to developer. Design is also flexible and different aspects of design can be mixed together or separated–for example, it is common Unix practice to write CLI program that doth the task and GUI that calleth CLI program as completely separated projects. As for task itself, it usually predefined, but can be often reformulated. For example, if thou hast to develop a calculator, thou shouldst think: needest thou really a calculator itself, or thou just needest a calculator in order to do something else? Is it possible to reformulate the task as “calculate something” instead of “write the calculator”? If it is, then the task is already done, since there are a lot of calculators already, and even more programs that can be used as calculators! So, the choice of task, careful design and smart implementation can really do wonders. On the design level, it may be a wise decision to go the Unix way so thou either needest not at all to implement GUI or at least canst do it as a completely separate task. On the implementation level, simply choosing Python instead of C can save thee weeks of work and give a lot of flexibility and portability for free.

Another important thing is that it is not that simple: task, design and implementation. In fact, every little bit of design is a separate task itself. For example, when thou decidest to have GUI (as a part of design process), thou automatically createst a new task: to design GUI, and whole philosophy applies to this task as well. So it all is a kind of a recursive process. Of course, on some level it becometh pointless to split thy work into further task / design / implementation parts, so recursion is not infinite ^_^ For example, while developing a calculator, thou decided (as a part of implementation process) to have a function that will divide two floating point values. So, “write function that divide two floating point values” is the new task. When designing this function, thou hast to think: how to name this function, what arguments should it accept, how it should handle errors (division by zero, for example), et cetera. On the implementation level, it is how it should work inside (that part that invisible from anywhere outside the function)–usually pretty simple part, but if thou wantest to write it in 8088 assembler without FPU, well, it might be not so simple ^_^

So, another important point is to clearly understand whether thou needest to think about design and implementation, or shouldst thou treat the whole task as atomic. It dependeth on a lot of things that thou shouldst decide for thyself. For example, if thou art writing simple shell command, there is usually no need to even design it–it is fine as long as it worketh. On the other hand, if it is a shell function to be put into ~/.bashrc, then it is maketh sense to at least design it such way so it will be easy to use in the future. Implementation details are usually unimportant in both cases, as long as thou indendest not to call it withing the long loop.

So, carefully think about task, carefully design the solution, carefully choose the implementation. Know that every sub-task is the task by itself. In other words: know what thou art trying to do in order to know what it should look like, think what it should look like in order to implement it properly, and implement as designed. At every point, there are sometimes unexpectedly simple solutions awaiting that were unnoticed simply because nobody ever thought about it this way.

There is a story about engineers that were trying to design a bulb for an electric lamp to be installed on a Moon research vehicle. It was a very hard research, and when they finally came up with something, they came to the chief engineer and asked, “How do you think, will it do?”. And he asked, “Hmm… Could you remind me please, why does the lamp need a bulb?” “Because otherwise, air will get in and the glower will burn out”, they replied. And then he asked, “Is there air on the Moon?” And then they understood that they were solving nonexistent problem. This was the story about carefully choosing the task. Note that it is not even about software development at all.

Design problems even need not an example to illustrate them. Creating GUI when nobody needeth it is one of the most frequent design errors. Why think I it is an error? Simply because it reduceth scriptability to zero. For example, if I have a CLI mp3 encoder (like lame), I can quickly write a script (even bat file in Windows will do!) that encodeth a thousand of files (and I did it many times, actually, maybe not for thousands of files, though)! If it is not CLI, but GUI, I have to live with what GUI giveth me, which is often not enough. And the most common design problem is not to design at all ^_^ This happeneth a lot–just take a look on some Internet URI and then read Cool URIs don’t change by Tim Berners-Lee, who is the WWW inventor, by the way. Some wise guys even think they need not URI at all–just make the whole thing in Flash! Great! No text operations (copy, paste, edit, etc.), no navigation except that was (if was) kindly provided by “designers”, no context menus… nothing except beautiful but stupid content! By the way, Flash designers (I mean those who designed Flash itself) might have given it a simple thought before making something that is intended to be used in the Web, but lacketh simple concept of URI. It is like creating a map where all objects are placed in the same location, with the attached list of “if you go there from here, then you will get to there” rules. Is not that nice? ^_- Those with weak imaginations, go and read XIV chapter of JRRT’s The Silmarillion (“Of Beleriand and its Realms”), it is very nice illustration to what I am saying.

Implementation errors are easiest to spot, so they tend to happen less frequently. But one common implementation error is to decide to design not parts of implementation, that is, to stop recursion too early. For example, if one thinketh about writing a function as about purely implementation thing, he may end up with defect implementation–for example, absent or inconvenient error handling. Another implementation error is to forget about what hath been designed–for example, trying to implement something that hath not even been designed, as with premature optimisation (the most common error of this kind).