From 57ef0c8319454f439e4bad3a26e2275cff0ab438 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B0=D0=BC=D0=BE=D1=83=D0=BA=D0=B8=D0=BD=20=D0=90?= =?UTF-8?q?=D0=BB=D0=B5=D0=BA=D1=81=D0=B5=D0=B9?= Date: Tue, 1 Jun 2010 11:20:08 +0400 Subject: [PATCH] Creating a calculate-client --- LICENSE | 202 ++ README | 16 + client/.calculate_directory | 1 + client/system/.calculate_directory | 1 + client/system/install/.calculate_directory | 1 + .../system/install/usr/share/config/kdm/kdmrc | 12 + client/system/uninstall/.calculate_directory | 1 + .../uninstall/usr/share/config/kdm/kdmrc | 12 + client/user/.calculate_directory | 1 + data/client | 51 + data/login.d/10client | 31 + i18n/cl_client_ru.mo | Bin 0 -> 11679 bytes lib/cl_keys.c | 59 + lib/cl_keys.i | 7 + pym/__init__.py | 1 + pym/cl_client.py | 1824 +++++++++++++++++ pym/cl_client_cmd.py | 140 ++ pym/cl_fill_client.py | 19 + pym/cl_share_cmd.py | 64 + pym/cl_sync.py | 132 ++ pym/cl_vars_client.py | 64 + scripts/cl-client-2.2 | 70 + scripts/cl-sync-2.2 | 63 + setup.cfg | 5 + setup.py | 121 ++ 25 files changed, 2898 insertions(+) create mode 100644 LICENSE create mode 100644 README create mode 100644 client/.calculate_directory create mode 100644 client/system/.calculate_directory create mode 100644 client/system/install/.calculate_directory create mode 100644 client/system/install/usr/share/config/kdm/kdmrc create mode 100644 client/system/uninstall/.calculate_directory create mode 100644 client/system/uninstall/usr/share/config/kdm/kdmrc create mode 100644 client/user/.calculate_directory create mode 100644 data/client create mode 100644 data/login.d/10client create mode 100644 i18n/cl_client_ru.mo create mode 100644 lib/cl_keys.c create mode 100644 lib/cl_keys.i create mode 100644 pym/__init__.py create mode 100644 pym/cl_client.py create mode 100644 pym/cl_client_cmd.py create mode 100644 pym/cl_fill_client.py create mode 100644 pym/cl_share_cmd.py create mode 100644 pym/cl_sync.py create mode 100644 pym/cl_vars_client.py create mode 100644 scripts/cl-client-2.2 create mode 100644 scripts/cl-sync-2.2 create mode 100644 setup.cfg create mode 100755 setup.py diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README b/README new file mode 100644 index 0000000..abf9c8b --- /dev/null +++ b/README @@ -0,0 +1,16 @@ +AUTHOR: Mir Calculate Ltd. + +INSTALL +------- + +calculate-client needs the following library version installed, in order to run: + Python >= 2.5 + python-ldap >= 2.0.0 + pyxml >= 0.8 + calculate-lib >= 2.2.0 + calculate-desktop >= 2.2.0 + +To install calculate-client, just execute the install script 'setup.py'. +Example: + + ./setup.py install diff --git a/client/.calculate_directory b/client/.calculate_directory new file mode 100644 index 0000000..ac25d9c --- /dev/null +++ b/client/.calculate_directory @@ -0,0 +1 @@ +# Calculate append=skip cl_name==calculate-client \ No newline at end of file diff --git a/client/system/.calculate_directory b/client/system/.calculate_directory new file mode 100644 index 0000000..285f21e --- /dev/null +++ b/client/system/.calculate_directory @@ -0,0 +1 @@ +# Calculate append=skip cl_pass_action==install||cl_pass_action==uninstall diff --git a/client/system/install/.calculate_directory b/client/system/install/.calculate_directory new file mode 100644 index 0000000..f1f0199 --- /dev/null +++ b/client/system/install/.calculate_directory @@ -0,0 +1 @@ +# Calculate append=skip cl_pass_action==install \ No newline at end of file diff --git a/client/system/install/usr/share/config/kdm/kdmrc b/client/system/install/usr/share/config/kdm/kdmrc new file mode 100644 index 0000000..d30cedb --- /dev/null +++ b/client/system/install/usr/share/config/kdm/kdmrc @@ -0,0 +1,12 @@ +# Calculate format=kde exists(/usr/share/config/kdm/kdmrc)==1 +[X-*-Core] +Reset=/usr/share/calculate/xdm/logout +Startup=/usr/share/calculate/xdm/login +#?cl_remote_host!=# +[X-*-Greeter] +MinShowUID=1000 +#cl_remote_host# +#?cl_remote_host==# +[X-*-Greeter] +MinShowUID=999 +#cl_remote_host# diff --git a/client/system/uninstall/.calculate_directory b/client/system/uninstall/.calculate_directory new file mode 100644 index 0000000..ac8e15b --- /dev/null +++ b/client/system/uninstall/.calculate_directory @@ -0,0 +1 @@ +# Calculate append=skip cl_pass_action==uninstall diff --git a/client/system/uninstall/usr/share/config/kdm/kdmrc b/client/system/uninstall/usr/share/config/kdm/kdmrc new file mode 100644 index 0000000..8e832a5 --- /dev/null +++ b/client/system/uninstall/usr/share/config/kdm/kdmrc @@ -0,0 +1,12 @@ +# Calculate format=kde exists(/usr/share/config/kdm/kdmrc)==1 +[X-*-Core] +Reset=/usr/share/config/kdm/Xreset +Startup=/usr/share/config/kdm/Xstartup +#?cl_remote_host!=# +[X-*-Greeter] +MinShowUID=1000 +#cl_remote_host# +#?cl_remote_host==# +[X-*-Greeter] +MinShowUID=999 +#cl_remote_host# diff --git a/client/user/.calculate_directory b/client/user/.calculate_directory new file mode 100644 index 0000000..34bad2e --- /dev/null +++ b/client/user/.calculate_directory @@ -0,0 +1 @@ +# Calculate path=~ name= chmod=0700 chown=#-ur_login-#:#-ur_group-# cl_pass_action==user diff --git a/data/client b/data/client new file mode 100644 index 0000000..23903c9 --- /dev/null +++ b/data/client @@ -0,0 +1,51 @@ +#!/sbin/runscript +# Copyright 2008-2010 Mir Calculate Ltd. http://www.calculate-linux.org +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +depend() { + use localmount + after bootmisc consolefont modules netmount + after readahead-list ypbind autofs openvpn gpm lircmd + after cupsd fbcondecor sshd cron wicd + before xdm + after acpid consolekit hald xfs +} + +start() { + # Mount remote Samba filesystems. + ebegin "Mounting domain resources" + # Identifing domain server by env file + local env_file=/var/calculate/calculate.env + if [[ -f $env_file ]] + then + local SERVER=$( sed -rn 's/^cl_remote_host\s*=\s*(\S+)$/\1/p' $env_file ) + for COUNT in $( seq 0 20 ) + do + ping -w2 -c1 $SERVER &>/dev/null && break || sleep 1 + done + fi + cl-client --mount + eend $? "Some samba remote resources to mount" +} + +stop(){ + ebegin "Unmount domain resources" + [[ $( mount | grep ' /var/calculate/remote ' ) ]] && + umount /var/calculate/remote + [[ $( mount | grep '/var/calculate/client-home on /home ' ) ]] && + umount /home + eend 0 +} + +# vim:ts=4 diff --git a/data/login.d/10client b/data/login.d/10client new file mode 100644 index 0000000..07e81c3 --- /dev/null +++ b/data/login.d/10client @@ -0,0 +1,31 @@ +#! /bin/sh +# Copyright 2010 Mir Calculate Ltd. http://www.calculate-linux.org +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +env-update +source /etc/profile +source /usr/share/calculate-2.2/xdm/functions + +if [ -e '/usr/bin/cl-sync-2.2' ]; +then + ERRORLOG=`/usr/bin/cl-sync-2.2 --progress --color=never --login $USER 2>&1` + # при неудачном выполнении, сгенерируем ошибку + if [ "$?" -gt "0" ]; + then + echo "$ERRORLOG" >> $FILE_LOG + xmes cl-sync "$ERRORLOG" + exit 1 + fi +fi +exit 0 \ No newline at end of file diff --git a/i18n/cl_client_ru.mo b/i18n/cl_client_ru.mo new file mode 100644 index 0000000000000000000000000000000000000000..053cdc55ec6a3b4cbcb63a8770efc9fc4f81b909 GIT binary patch literal 11679 zcmcJUdyHJweaCMKB;Yh9;ZZ``h7%mJcI;Vfz=>JFVdGstz-uq-HNk*Ou4nG<4xYVt zdS}M=Hf@9LLPAY3REZLzfP<*|j?qp6`pqkcrDlmu1Rng_yO)82R{sc4g7iVRZ#2y27C|rBk)7ue}h+o z?_c8HF9VlzzYf&%DeyXQKWM;bz&pWX;4gsx2L2NGQ&9X||8CFg2X6J`R}8KPzW^HWHBkKjFL){VGjPp2_>D3SaX$bP+rhKoGVm&l`U&t3a2xm;a0B=k z30{Q~?&p3dcrQ2$D$d>jSAt6sT69LiyTKVydN~OWfIk9%6KDLxLy;4t_n;A-$cz!l()IKe&OR&X8o9EdA>uYtFN zuY>aM8`wnhuLp50?@Qn)_y^!!;7>sDxdx}X6MPs%<=%7PN5MY>?*so1jKJjt=Ph~< z>iu7WTK^M}|Gf_y$Im)Y{QoAn3!G2xuf~Yd-@OS|LD6|06#st?DsTNQsQCJ6a=(;d z(7yMAli+WGTK7Y66Zk%aod6#M<&WP2*MnE!tOMW(D7v2qS>nxt();&7&AXJ%Z~<=< zD7`!aiq7+(?E5;1X}qfuR`>nj5cn`CKY9^70RAT^{wi!Hdw(As25&{^gWwFf8GHj= z0&c)L?g8%urT4Fb;^!Nn=>Hge5nPL~n*aBp^z=F?Km9LI`)|f6z70MHZUA@TtWSWy z4~~N?VEQufF%TE^o&etgDkgPlPwl6$8`0 z+AoIQV7XkHF@9;EKLfcqnkbfninrbmO&C>8-=1QrWNsponos;f!T42E2qyemscPa{ zSPJ$ArS^MEudf(Zs(z_-bG}pz!fIbHWxsQt<7Ux$$R0Oow6-b)rJ#!JaWuU+Kf(SW z-&&!Y8cX8JOxS)p9o52WYmU7$+x_X?zKMfMREzV0dFZCfuGEaZ0Xk{j$H8>OlR`00 zKdtyY^JTRb=cX89HfB22H&t2-J2^=1F>ZA#O|kjhlDw(X;?7$?<%g5O1z6iG%loB# zt>jmO9DMB!;^p4@XnHydO;oNHqY$B@=`t#c4Ti?)T8?5FX})t37ZTN5Y3%zrfU=p2 zrmZ&cwJ4skt|zI(V4u^5pU*pu+v$bYe32L`#>p}S#KD8P?#d-0q z*;w2$o4mkXs(!3j$%9(C;AW<2dc*tu>9V41V^Hn(5W8Q>M>Zw8L^nArMTYq(7O&oj)x7Erf|OEz)K!IXAVJlQ#tzZo7C zr%akpYZYZ7Bc*iF7NLtACsrfpJFTmU*JRoq~M_WG9M zyB@bZu|2gjINQ%1cZpA=NGDtur+r*^GLC9xvuv_hK#R+}XVr?NLS*1|>eXI9F8aGm zLB^wsx6MVF@ndqs?h2`~-y3Z*G3w&IY3Jr`*!KLD!bD6BnCkZOmq;i!L7-2~hDkZT)9ExeeJnL!~phZ4YqKf_Z=v@u7 z`J}w;rlOUDFevnU+lhr{`H{bsB2pFG6@s2uVNfNI_q1NxSj0|=>1+w}#);&7Ghe~D zveX92J$VyHQI#d!oo7vC;Fnc}gt>&>es4UU@pf3vG$RKYTfx!ws`)&{&5n@R zj3yO1g(P&n9TloLe*+a2X&&ZV%4|KCR4$}=y(28{N8Rz>Vm?`!WLho98WY9IS{xKS zm2kwkm}VmB4<3yYBU|0I%%vbG$L&DBplqN?HLEmdiKbpWIIYG}$?TgVt08_qD&aba zAl3JqPi-3BN|Q4qWAuge9&z7F)8`oE7gPKES~cqP3dKsf#5~(5O$DX04OVJM)l0lU zE;$_$ip7wh)1>Ms?DyEodAVcmA!U$iu&ZTITR>cRWR%k^iDV_^=JRF8l}!m{XjJfM zEu>W=(DxPT0+fCGO3`F7?6VT`Gpbelyk^x_t1zHsSdNQf)iiTZ#Vi}&IXe8v{expB zN0V0?n5fau@p`$JBt}s_PnYO7BPN-CaegC>BiIDBp8`-JrLIJc>Xzt8ZQJHLp~@7E zEABLj6I9G@KejsFQ-C!U)LPkl(-~%&aze#KA?>WyS)v=0%UQ)O_uk9|9?cFaly7^Z zar7wlGPk*qyFZAPaRI(ygJD^Bg$^lX-i<9_upjdWA)h%9vVlq-Y|WZ%?|SZZ2c8T zEVKxUv*SED)p)w`IZJCn`<&E-vvo_oamcG5wH!7#JJUGaJTcYJiD=`u;r~>9!K*(n zxel>-mix18`jn~9H4ZxsNN%{`k)$|n6>+?AD0RxpTrW7thHtK&UNYV8f(OjYeXMK?^;bH`HlE59 zvSourWefEaZIzvoi7!H7JqFC&GQ-a9oSA9VPRFxcW>d|Wm~`&wb{-GI5gG^F33ZX3 z6d3a|xPl-tIf*W7cDLS+GhK^MDlYl8(0E!oGIQwxW<1Uu8{f{)Q}&mWcIAP@uoz=5 z6>s(VO!5#v*b3D1=&NViV*K_JT#UiC=SdeWl}is_#*bWocwSe4|TB0y%XFd{% zWPFi*PS})Ylc`ILvyIP_c%H@`c+=}6JhTa0iF%IlilUDM3!{2O4BPl)@_P|HZ`jV*phPyjk-XX!`WeIaVl?Z1sly$ zWvmgz<56olTL2}ORg3JT!b2Wcyd1+9RejKsB9s84GA^i0r31sOe~DKwWBfztKxsam zUnS{c@kM@+Az!Hf5f1w-j`IvRD0_jOTXYsWwQINUF@ucNxl)b?>mBK|YmUm7a|G8F zsQ53!s|&Se2LR=ywlgrZ99xYxRoWfdNm~uv{tP&il zw+({H_J99%2Rg*g;scVen^5c zJ(xT}snP~q#u9gO!#L8a)vcneS<^bG%XU`v?dq4sL|ajo%uC#>`;*Dx^*q^(*PZex zUG&J9mS-_H&tS_L%?Y_cc1c*t)~yQ@%d5*T=d^44%U2Iq_=i4!p>7U8Z;310kTg}) zE4Cc)T;ln*z~VuN)f%{TI&0e*TL_H`xh=~2dyqElkP2ddb86xUM|rH?TUF$cP7BJl z8F8GNXYG?c3oFU&cEedVRsJT$!7~oS`6u}>XCtJQq;OE}id-stO1vB#=g@%ZV2c!yB zVmbg9B$oAT2X$rL#>;7xrNbWQx?_#QuFtehpUMLCiJ_OAU1SkL+=im{r?wc%6Vf_H zP!6;+b?RisJk)hOx==b+3guFj^j;dzBW(KYdBIgXv%#|(NH+y8B&?59*}8&Ckh!;J9p` zdiPC%Hb*M$s!pAc94oD__8Da^I^^{r5GC~J^{dQX{36I!P7^39g2t>3@QU#&clQ#RK39H$=rLrC`l`)wg( smRUAz$hw4MEO#nUgtlA>%XXAPct73#Clz$@wtKtB*LM~AMUtoYf1=TJ$^ZZW literal 0 HcmV?d00001 diff --git a/lib/cl_keys.c b/lib/cl_keys.c new file mode 100644 index 0000000..0101051 --- /dev/null +++ b/lib/cl_keys.c @@ -0,0 +1,59 @@ +// Copyright 2007-2010 Mir Calculate Ltd. http://www.calculate-linux.org +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +// для пароля +#include + +// для strcpy strlen +#include + +// для работы с ключами +#include + +char* getKey(char *login) +{ + char buffer[255]; + memset(buffer,0,sizeof(buffer)); + int ret; + // ищем номер пользовательского ключа + ret = request_key("user", login, NULL, 0); + if (ret < 0) + { +// printf ("id_key not found\n"); + return ""; + }; + + // Возвращаем значение ключа + ret = keyctl_read(ret, buffer, sizeof(buffer)); + if (ret < 0) + { +// printf("error keyctl_read\n"); + return ""; + }; + return buffer; +}; + +int clearKey(char *login) +{ + char *buffer; + buffer = "XXXXXXXX"; + key_serial_t dest; + dest = KEY_SPEC_USER_SESSION_KEYRING; + if (add_key("user", login, buffer, strlen(buffer), dest)!=-1) + return 0; + else + return 1; +}; diff --git a/lib/cl_keys.i b/lib/cl_keys.i new file mode 100644 index 0000000..4143e09 --- /dev/null +++ b/lib/cl_keys.i @@ -0,0 +1,7 @@ +%module cl_keys +%inline %{ +/* Put header files here or function declarations like below */ +extern char* getKey(char*); +extern int clearKey(char*); +%} +%include cl_keys.c \ No newline at end of file diff --git a/pym/__init__.py b/pym/__init__.py new file mode 100644 index 0000000..8d1c8b6 --- /dev/null +++ b/pym/__init__.py @@ -0,0 +1 @@ + diff --git a/pym/cl_client.py b/pym/cl_client.py new file mode 100644 index 0000000..b4938c9 --- /dev/null +++ b/pym/cl_client.py @@ -0,0 +1,1824 @@ +#-*- coding: utf-8 -*- + +# Copyright 2010 Mir Calculate Ltd. http://www.calculate-linux.org +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +__version__ = "2.2.0" +__app__ = "calculate-client" + +import os +import re +import sys +import pwd + +import subprocess +import ldap +import time +import types + +import getpass + +from cl_lang import lang +from cl_template import template, iniParser +from cl_datavars import DataVars +from cl_print import color_print +from cl_ldap import ldapUser +from client.progressbar import ProgressBar +from cl_utils import runOsCommand, getpathenv, getModeFile, removeDir +from _cl_keys import getKey, clearKey + +lang().setLanguage(sys.modules[__name__]) + +class DataVarsClient(DataVars): + """Хранение переменных""" + + def flClient(self, **args): + '''Заполнить конфигурацию переменных, для десктопа''' + # Имя секции в calculate.env + envSection = "client" + # заполнить переменные окружения алгоритмом по умолнанию + self.importData(envSection, ('cl_vars_client','cl_fill_client')) + + +class share(color_print): + """Общие методы""" + # Объект хранения переменных + clVars = False + + def isRoot(self, printError=True): + """Определяет является ли пользователь root""" + if os.getuid() == 0 and os.getgid() == 0: + return True + else: + if printError: + self.printERROR(_("The user is not root")) + return False + + def createClVars(self, clVars=False): + """Создает объект Vars""" + if not clVars: + clVars = DataVarsClient() + clVars.flClient() + clVars.flIniFile() + # Устанавливаем у объекта объект Vars + self.clVars = clVars + return True + + def applyTemplatesFromSystem(self): + """Применяем шаблоны для cистемы""" + # Cоздаем объект обработки шаблонов + clTempl = template(self.clVars) + # Объединяем шаблоны + dirsFiles = clTempl.applyTemplates() + if clTempl.getError(): + self.printERROR(clTempl.getError().strip()) + return False + else: + return dirsFiles + + def printVars(self, opts): + """Печать существующих переменных""" + if opts == ["all"]: + self.clVars.printVars() + else: + self.clVars.printVars(opts) + +class RsyncProgressBar: + '''Объект запуска rsync для получения количества созданных файлов и + при необходимости вывода progressbar + ''' + + # получение номера передаваемого файла из инф потока rsync + senderre = re.compile("\[sender\] i=(\d+) ", re.S) + # получение номера получаемого файла из потока rsync + receiverre = re.compile("recv_generator\(.+,([0-9]+)\)", re.S) + pipe = None + maximum = 1 + copyStarting = False + + def __init__(self, title, secondtitle, rsyncstr, maximum=1): + self.title = title + self.secondtitle = secondtitle + self.maximum = maximum + self.rsyncstr = rsyncstr + self.progress = ProgressBar(title,1) + self.progress.setMaximum(self.maximum) + + def getFilesNum(self): + '''Получить количество файлов созданных генератором''' + if self.pipe: + return self.value + + def getExitCode(self): + '''Получить код выхода rsync''' + if self.pipe: + return os.WEXITSTATUS(self.pipe.wait()) + + def runsilent(self): + '''Запустить rsync без progressbar''' + self.pipe = subprocess.Popen(self.rsyncstr, stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + close_fds=True, shell=True) + while True: + s = self.pipe.stdout.readline() + if len(s) == 0: + break + q = self.receiverre.search(s) + if q: + self.value = int(q.groups()[0]) + + def run(self): + '''Запустить rsync с progressbar''' + self.progress.openDialog(self.title,0) + self.pipe = subprocess.Popen(self.rsyncstr, stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + close_fds=True, shell=True) + oldpercent = 0 + while True: + s = self.pipe.stdout.readline() + if len(s) == 0: + break + q = self.senderre.search(s) + if q: + maximum = int(q.groups()[0]) + if self.maximum < maximum: + self.maximum = maximum + continue + q = self.receiverre.search(s) + if q: + if not self.copyStarting: + self.progress.shutdownDialog() + self.progress.openDialog(self.secondtitle) + self.progress.setMaximum(self.maximum) + self.copyStarting = True + value = int(q.groups()[0]) + self.progress.setValue(value) + + def close(self): + self.progress.shutdownDialog() + +class ldapData(ldapUser): + """Методы для LDAP""" + + def addDN(self, *arg): + """Складывает текстовые элементы DN""" + DNs = [] + for dn in arg: + if dn: + DNs.append(dn) + return ','.join(DNs) + + def getReplDN(self): + """Получаем DN ветки хранения последнего посещенного сервера + + В ветке находятся ((имя пользователя, имя системы, хост) ....) + """ + usersDN = self.getUsersDN() + if usersDN: + partDN = "ou=Worked,ou=Replication,ou=LDAP" + servicesDN = "ou=Services" + baseDN = usersDN.rpartition(servicesDN+",")[2] + replDN = self.addDN(partDN, servicesDN, baseDN) + return replDN + return False + + def searchPrevHost(self, userName, osLinuxShort): + """Находит сервер к которому был подключен пользователь""" + connectData = self.getBindConnectData() + if connectData: + bindDn, bindPw, host = connectData + replDN = self.getReplDN() + # cтрока для поиска в служебной ветке репликации + userAndOsName = "%s@%s"%(userName,osLinuxShort) + findAttr = "uid=%s"%userAndOsName + # Соединяемся с LDAP + if not self.ldapConnect(bindDn, bindPw, host): + return False + resSearch = self.ldapObj.ldapSearch(replDN, ldap.SCOPE_ONELEVEL, + findAttr, ["host"]) + return resSearch + return False + + def getNameRemoteServer(self,userName, osLinuxShort): + """Если профиль на удаленном сервере, то выдать DNS имя этого сервера + """ + searchPrevHost = self.searchPrevHost(userName, osLinuxShort) + if searchPrevHost and searchPrevHost[0][0][1].has_key('host'): + prevHost = searchPrevHost[0][0][1]['host'][0] + else: + prevHost = None + # если местоположение актуального профиля найти не удалось + # или его местоположение не на локальном сервере + if not prevHost or prevHost == osLinuxShort: + return False + else: + return prevHost + + def isRepl(self): + """Включена ли репликация на сервере""" + connectData = self.getBindConnectData() + if connectData: + bindDn, bindPw, host = connectData + usersDN = self.getUsersDN() + partDN = "ou=Replication,ou=LDAP" + servicesDN = "ou=Services" + baseDN = usersDN.rpartition(servicesDN+",")[2] + replDN = self.addDN(partDN, servicesDN, baseDN) + findAttr = "ou=Worked" + # Соединяемся с LDAP + if not self.ldapConnect(bindDn, bindPw, host): + return False + resSearch = self.ldapObj.ldapSearch(replDN, ldap.SCOPE_ONELEVEL, + findAttr, + [findAttr.partition("=")[0]]) + if resSearch: + return True + return False + +class client(share): + """Методы работы для подключения пользователя к серверу и синхронизации""" + # Объект для поиска пользовательских данных в LDAP + ldapDataObj = ldapData() + # Путь относительно домашней директории в котором находятся + # конфигурационные файлы + pathConfig = ".calculate" + # Конфигурационный файл для клиента + configFileDesktop = os.path.join(pathConfig, "desktop.env") + # Конфигурационный файл для сервера + configFileServer = os.path.join(pathConfig, "server.env") + # Файл - список файлов профиля пользователя + listTemplFile = os.path.join(pathConfig, "files.txt") + + logOutFile = ".logout" + # Файлы котороые не удаляются при очистке домашней директории + skipHomeFile = ["Home","Disks","FTP",logOutFile,configFileDesktop] + + # словарь опций сервисов из /var/calculate/remote/calculate.env + optionsInfo = {} + + + def removeVars(self): + """Удаление переменных шаблонов + + при удалении пакета и выходе из домена""" + self.clVars.Delete("cl_remote_host", "local") + self.clVars.Delete("cl_remote_pw", "local") + self.clVars.Delete("os_remote_auth") + self.clVars.Delete("os_remote_client") + self.clVars.Set("cl_remote_host", "", True) + self.clVars.Set("cl_remote_pw", "", True) + self.clVars.Set("os_remote_auth", "", True) + self.clVars.Set("os_remote_client", "", True) + + def isTwoSessionsUser(self, userName): + """Проверка на повторный вход пользователя""" + xSession = 0 + foundTwoSession = False + reFoundUser = re.compile("%s\s+:\d+\s+"%(userName)) + resWho = self.execProg("who",False,False) + if resWho and type(resWho) == types.ListType: + for string in resWho: + if reFoundUser.search(string): + xSession +=1 + if xSession>1: + foundTwoSession = True + self.printERROR(\ + _("Second X session for user %s can not be opened.")\ + %userName) + break + return foundTwoSession + + def isDomain(self): + """Находится ли компьютер в домене""" + foundMountRemote = os.path.ismount("/var/calculate/remote") + foundMountHome = os.path.ismount("/home") + remoteHost = self.clVars.Get("cl_remote_host") + if remoteHost and foundMountRemote and foundMountHome: + return True + self.printERROR(_("The computer is not in domain")) + return False + + def getUserMountResources(self, userName, homeDir, flagRemoteServer): + """Получение монтируемых ресурсов для пользователя""" + home = os.path.split(homeDir)[0] + dictResources = {} + names = [] + # Pесурс share + name = "share" + dictResources[name] = {"resource":"share", + "path":os.path.join(homeDir,"Disks")} + names.append(name) + # Ресурс шаблонов + name = "profile" + dictResources[name] = {"resource":"unix", + "path":os.path.join(home,"."+userName)} + names.append(name) + # Ресурс - домашняя директория (файлы пользователя на сервере) + name = "home" + dictResources[name] = {"resource":"homes", + "path":os.path.join(homeDir,"Home")} + names.append(name) + if self.getInfoService("ftp", "host"): + # Ресурс ftp + name = "ftp" + dictResources[name] = {"resource":"ftp", + "path":os.path.join(homeDir,"FTP")} + names.append(name) + if flagRemoteServer: + # Ресурс шаблонов на удаленном сервере + name = "remote_profile" + dictResources[name] = {"resource":"unix", + "path":os.path.join(home,"."+userName+"."+"remote")} + names.append(name) + return names, dictResources + + def mountSambaRes(self,host,userName,userPwd,uid,gid,res,path, + mountUidList=['ftp','home','share']): + """Монтирует Samba ресурсы""" + if res in mountUidList: + # Монтируем директории c uid + mountStr = "mount -t cifs -o user=%s,uid=%s,gid=%s,noperm"\ + %(userName,uid,gid) +\ + " //%s/%s %s" %(host, res, path) + else: + # Монтируем директории + mountStr = "mount -t cifs -o user=%s"%(userName)+\ + " //%s/%s %s" %(host, res, path) + textLine = self.execProg(mountStr, None, True, {"PASSWD":userPwd}) + return textLine + + def mountSleepRes(self,host,userName,userPwd,uid,gid,res,path): + """Монтирует ресурс при неудаче задержка потом повторное монитрование""" + textLine = self.mountSambaRes(host,userName,userPwd,uid,gid,res,path) + if not (textLine is None): + # Проверяем на монтирование директории + if os.path.ismount(path): + textLineUmount = self.umountSleepPath(path) + if not textLineUmount: + return False + i = 0 + sleeps = [0.5, 2, 5] + while (i/dev/null"%(srcDir, destDir)) != 0: + self.printERROR(_("Can not move %s")%srcDir + " " +\ + _("to %s")%destDir) + return False + return True + + def upgradeTemplateClient(self, userName, userHome, pathTemplates): + """Переносит данные клиента в директорию без точки + например: было /home/.user/.CLD стало /home/.user/CLD + + Перед вызовом этого метода обязательно должен быть определен атрибут + объекта self.clVars - объект переменных + """ + # Директория хранения старых шаблонов + home = os.path.split(userHome)[0] + if os.path.exists(pathTemplates): + osLinuxShort = self.clVars.Get("os_linux_shortname") + pathNewTemplate = os.path.join(pathTemplates, osLinuxShort) + pathOldTemplate = os.path.join(pathTemplates, "."+osLinuxShort) + if not os.path.exists(pathNewTemplate) and\ + os.path.exists(pathOldTemplate) and\ + os.listdir(pathOldTemplate): + # Переносим профиль + if not self.copyTemplateDir(pathOldTemplate, + pathNewTemplate): + return False + if not os.path.exists(pathNewTemplate): + # Создаем директорию для хранения профиля + os.mkdir(pathNewTemplate) + os.chmod(pathNewTemplate, 0700) + if os.path.exists(pathOldTemplate) and\ + not os.listdir(pathOldTemplate): + os.rmdir(pathOldTemplate) + return True + + def createUserDir(self, uid, gid, userDir, mode=0700): + """Создание пользовательской директории""" + if not os.path.exists(userDir): + os.makedirs(userDir) + if mode: + os.chmod(userDir,mode) + os.chown(userDir,uid,gid) + return True + else: + self.printERROR(_("Path %s exists") %userDir) + return False + + def syncUser(self, userName, userHome, sync, uid, gid, homeTemplate, \ + progress=False, host="default"): + """Синхронизация пользовательских настроек + + Перед вызовом этого метода обязательно должен быть определен атрибут + объекта self.clVars - объект переменных + """ + flagError = False + execStr = "" + if sync == "login": + # исключаемые пути при синхронизации + # /.local/share/akonadi/db_data + # хранит базу acanadi + # /.mozilla/firefox/calculate.default/urlclassifier3.sqlite + # хранит БД для firefox + # /.local/share/mime/mime.cache + # отключение ошибочного кэширования изображений + # /.kde4/share/apps/nepomuk/repository/main/data + # база nepomuk + # /.VirtualBox + # содержит данные о виртуальных машинах + if os.path.exists(userHome) and\ + os.path.exists(homeTemplate): + execStr = '/usr/bin/rsync --delete-excluded --delete \ +--exclude="/.googleearth" --exclude="/.kde4/share/config/phonondevicesrc" \ +--exclude="*~" --exclude="/Home" --exclude="/Disks" --exclude="/FTP" \ +--exclude="/.local/share/akonadi/db_data" --exclude="/.VirtualBox" \ +--exclude="/.mozilla/firefox/calculate.default/urlclassifier3.sqlite" \ +--exclude="/.local/share/mime/mime.cache" \ +--exclude="/.kde4/share/apps/nepomuk/repository/main/data" \ +--exclude="/.logout" \ +--exclude="/.Xauthority" \ +--exclude="/.thumbnails" \ +--exclude="/.mozilla/firefox/*/Cache" \ +--filter="P /.googleearth" --filter="P /Home" --filter="P /Disks" \ +--filter="P /.local/share/akonadi/db_data" --filter="P /.VirtualBox" \ +--filter="P /.mozilla/firefox/calculate.default/urlclassifier3.sqlite" \ +--filter="P /.local/share/mime/mime.cache" \ +--filter="P /.kde4/share/apps/nepomuk/repository/main/data" \ +--filter="P /.logout" \ +--filter="P /.Xauthority" \ +--filter="P /.thumbnails" \ +--filter="P /.mozilla/firefox/*/Cache" \ +--filter="P /FTP" -a -x -v -v -v -v %s/ %s/' %(homeTemplate,userHome) + elif sync == "logout": + if os.path.exists(userHome) and os.listdir(userHome) and\ + os.path.exists(homeTemplate): + execStr = '/usr/bin/rsync --delete-excluded --delete \ +--exclude="/.googleearth" --exclude="/Home" --exclude="/Disks" --exclude="/FTP"\ + --exclude="*~" --exclude="/.kde4/cache-*" --exclude="/.kde4/tmp-*" \ +--exclude="/.local/share/akonadi/db_data" --exclude="/.VirtualBox" \ +--exclude="/.mozilla/firefox/calculate.default/urlclassifier3.sqlite" \ +--exclude="/.local/share/mime/mime.cache" \ +--exclude="/.Xauthority" \ +--exclude="/.kde4/share/apps/nepomuk/repository/main/data" \ +--exclude="/.kde4/socket-*" --exclude="/.kde4/share/config/phonondevicesrc" \ +--exclude="/.thumbnails" \ +--exclude="/.mozilla/firefox/*/Cache" \ +-a -x -v -v -v -v %s/ %s/'%(userHome,homeTemplate) + else: + self.printERROR(_("Method syncUser: option sync=%s incorrect")\ + %str(sync)) + return False + if execStr: + host = "" + host +"" + rsync = RsyncProgressBar(\ + _("Receiving file list from %s") % host + " ...", + _("Downloading the user profile from %s") % host \ + + " ...", execStr) + pathConfig = os.path.join(homeTemplate, + self.pathConfig) + # Удаляем предыдущий ini файл + prevIniFile = os.path.join(homeTemplate,".calculate.ini") + if os.path.exists(prevIniFile): + os.remove(prevIniFile) + # Создаем директорию для конфигурационных файлов + if not os.path.exists(pathConfig): + self.createUserDir(uid, gid, pathConfig, mode=False) + configFileName = os.path.join(pathConfig, self.configFileDesktop) + if sync == "login": + # получить переменную files из секции Rsync файла + # .calculate.ini + try: + numfiles = iniParser(\ + configFileName).getVar('rsync','files') + if numfiles is False: + if os.path.exists(configFileName): + os.remove(configFileName) + numfiles = 1 + else: + numfiles = int(numfiles) + except: + numfiles = 1 + rsync.maximum = numfiles + if progress: + rsync.run() + else: + rsync.runsilent() + if sync == "logout": + rsync.runsilent() + try: + if iniParser(configFileName).setVar('rsync', + {'files':rsync.getFilesNum()}): + os.chmod(configFileName, 0600) + os.chown(configFileName,uid,gid) + except: + pass + rsync.close() + if rsync.getExitCode() != 0: + try: + if iniParser(configFileName).setVar(\ + 'rsync',{'exitcode':rsync.getExitCode()}): + os.chmod(configFileName, 0600) + os.chown(configFileName,uid,gid) + except: + pass + self.printERROR(_("Can not execute rsync") + " " + str(sync) +\ + " ...") + flagError = True + else: + if sync == "login": + if not (os.path.exists(userHome)): + self.printERROR(_("Directory %s not exists")%userHome) + else: + self.printERROR(_("Directory %s not exists")%homeTemplate) + elif sync == "logout": + if not (os.path.exists(userHome)): + self.printERROR(_("Directory %s is empty or not exists")\ + %userHome) + else: + self.printERROR(_("Directory %s not exists")%homeTemplate) + flagError = True + if flagError: + return False + else: + # Домашняя директория и директория хранения профиля + changeDirs = [userHome, homeTemplate] + for changeDir in changeDirs: + # Получаем права на директорию + mode = getModeFile(changeDir, mode="mode") + # Если права не равны 0700 меняем их + if mode != 0700: + os.chmod(changeDir,0700) + return True + + def clearHomeDir(self, homeDir): + """Очистка домашней директории от файлов""" + rmFiles = list(set(os.listdir(homeDir))-\ + set(self.skipHomeFile)) + for rmFile in rmFiles: + delFile = os.path.join(homeDir,rmFile) + if os.path.islink(delFile): + os.unlink(delFile) + elif os.path.isfile(delFile): + os.remove(delFile) + elif os.path.isdir(delFile): + if not removeDir(delFile): + return False + elif stat.S_ISSOCK(os.stat(delFile)[stat.ST_MODE]): + os.remove(delFile) + return True + + def syncLoginTemplate(self, host, userName, homeDir, homeTemplate, uid, gid, + progress, flagClearHomeDir=True): + """Синхронизация профиля пользователя с сервера + в переменной 'cl_remote_host' хост для прогрессбара + """ + home = os.path.split(homeDir)[0] + # Если на текущем сервере в ресурсе unix есть файлы + # то синхронируем настройки + if os.listdir(homeTemplate): + if not self.syncUser(userName, homeDir, "login", uid, gid,\ + homeTemplate, progress=progress, + host=host): + return False + else: + if flagClearHomeDir: + # Удаляем ненужные файлы (очищаем домашнюю директорию) + if not self.clearHomeDir(homeDir): + return False + return True + + def getDataInConfig(self, section, listVars, objConfig): + """Читаем переменные listVars из секции section конф. файла""" + varsConfig = {} + for varName in listVars: + varsConfig[varName] = objConfig.getVar(section,varName) + if objConfig.getError(): + return False + return varsConfig + + def setServerCommand(self, command, varsCommand, fileConfig, + uid=None, gid=None): + """Установить команду для сервера""" + pathConfig = os.path.split(fileConfig)[0] + # Создаем директорию если ее нет + if not os.path.exists(pathConfig): + os.makedirs(pathConfig) + if not uid is None and not gid is None: + os.chown(pathConfig, uid, gid) + objConfig = iniParser(fileConfig) + varsRun = {"run":"on"} + varsRun.update(varsCommand) + if not objConfig.setVar(["command"]+command, varsRun): + self.printERROR(_("Can not write variables in file %s")\ + %fileConfig) + return False + if not uid is None and not gid is None: + os.chown(fileConfig, uid, gid) + return True + + def foundArchFile(self,strCurrentTime,archPathProcess, + archPathSuccess, progress=False, + remoteServer=False): + """Поиск архива профиля пользователя""" + # Ищем архив профиля + # Задержки при поиске файла архива .process + if progress: + title = _("Packing the archive at the server") + if remoteServer: + title += " " + remoteServer + title += " ..." + progressObj = ProgressBar(title, 0) + progressObj.openDialog(title, 0) + sleepsFindProcess = [0.1, 0.2, 0.5] + # Задержки при проверке размера архива + sleepsArch = [0.5, 1, 2] + lenFP = len(sleepsFindProcess) + lenA = len(sleepsArch) + i = 0 + # Ждем появления файла архива + while(not (os.path.exists(archPathProcess) or\ + os.path.exists(archPathSuccess)) and i=lenA: + # rsync схема синхронизации + if progress: + progressObj.shutdownDialog() + return False + startSize = os.stat(archPathProcess).st_size + time.sleep(sleepsArch[i]) + if os.path.exists(archPathSuccess): + break + endSize = os.stat(archPathProcess).st_size + if startSize == endSize: + i += 1 + if progress: + progressObj.shutdownDialog() + return True + + def fileReader(self, fileName, stdin, progressObj=False): + """Читает файл блоками в поток""" + fd = os.open(fileName, os.O_RDONLY) + if progressObj: + currSize = 0 + # Размер файлового буфера в байтах + buffSize=131072 + dataBlock = os.read(fd, buffSize) + while dataBlock: + if progressObj: + currSize += len(dataBlock) + progressObj.setValue(currSize) + stdin.write(dataBlock) + dataBlock = os.read(fd, buffSize) + stdin.close() + os.close(fd) + + def unpackTemplate(self, homeDir, archFile, progress=False,\ + remoteServer=False): + """Распаковка архива в домашнюю директорию пользователя""" + # Создаем прогрессбар + if progress: + title = _("Unpacking the archive from server") + if remoteServer: + title += " " + remoteServer + title += " ..." + progressObj = ProgressBar(title) + archiveSize = os.stat(archFile).st_size + progressObj.setMaximum(archiveSize) + execStr = "tar -C '%s' -xzf -" %homeDir + # Выполняем разархивацию + pipe = subprocess.Popen(execStr, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + close_fds=True, + env=os.environ, shell=True) + # Чтение файла в поток + if progress: + self.fileReader(archFile, pipe.stdin, progressObj) + else: + self.fileReader(archFile, pipe.stdin) + ret = pipe.wait() + pipe.stdout.close() + pipe.stderr.close() + if progress: + progressObj.shutdownDialog() + if ret: + self.printERROR(_("Can not execute %s") %execStr) + self.printERROR(_("Can not unpack %s") %archFile) + return False + return True + + def setVarToConfig(self, nameSection, varsDict, configFileName, uid, gid): + """Записывает переменную в файл конфигурации""" + # Создаем директорию для конфигурационных файлов + pathConfig = os.path.split(configFileName)[0] + if not os.path.exists(pathConfig): + self.createUserDir(uid, gid, pathConfig, mode=False) + try: + if iniParser(configFileName).setVar(nameSection, varsDict): + os.chmod(configFileName, 0600) + os.chown(configFileName,uid,gid) + except: + return False + return True + + def createUserFile(self, fileName, fileTxt, uid, gid, mode=0644): + """Создает пользовательский файл с содержимым + + Если директория файла не существует то ошибка + """ + userDir = os.path.split(fileName)[0] + if not os.path.exists(userDir): + self.printERROR(_("Path %s not exists") %userDir) + return False + FD = open(fileName, "w") + modeFile, uidFile, gidFile = getModeFile(fileName) + if mode != modeFile: + os.chmod(fileName, mode) + if (uidFile, gidFile) != (uid, gid): + os.chown(fileName,uid,gid) + if fileTxt: + FD.write(fileTxt) + FD.close() + return True + + def mountUserResAndSync(self, userName, sync=True, progress=False): + """Монтирование пользовательских ресурсов и синхронизация настроек""" + # Проверяем на root + if not self.isRoot(): + return False + # Проверка на повторный вход пользователя + if self.isTwoSessionsUser(userName): + return False + domain = self.clVars.Get("cl_remote_host") + hostAuth = self.clVars.Get("os_remote_auth") + # В случае компьютера вне домена + if not hostAuth or not domain: + self.printSUCCESS(_("To be used local profile.")) + return True + # Проверим что компьютер в домене и смонтирован [remote] + connectDomain = self.isDomain() + if not connectDomain: + # Отмонтируем пользовательские ресурсы в случае ошибки + self.umountUserRes() + return False + # Информация о пользователе из LDAP + userLdapInfo = self.ldapDataObj.getUserLdapInfo(userName) + if userLdapInfo: + uid = int(userLdapInfo['uid']) + gid = int(userLdapInfo['uid']) + homeDir = userLdapInfo['home'] + else: + self.printERROR(_("Can not found user %s in LDAP")%userName) + self.umountUserRes() + return False + rootPath = self.clVars.Get('cl_root_path') + # Реальный путь к домашней директории + homeDir = os.path.join(rootPath, homeDir[1:]) + # Файл хранения настроек пакета + configFileName = os.path.join(homeDir, self.configFileDesktop) + # При отсуствии создаем домашнюю директорию + if not os.path.exists(homeDir): + os.makedirs(homeDir) + os.chown(homeDir,uid,gid) + os.chmod(homeDir,0700) + # записываем в конфигурационный файл статус "в процессе" + self.setVarToConfig("main", {"status_sync":"process"}, + configFileName, uid, gid) + # Получаем пароль пользователя из ключей ядра + userPwd = getKey(userName) + if not userPwd: + self.printERROR(_("Not found user password")) + # Отмонтируем пользовательские ресурсы в случае ошибки + self.umountUserRes(homeDir) + return False + # Флаг ошибки + flagError = False + # Имя удаленного сервера + remoteServer = "" + # В случае включения репликации на сервере True + replOn = self.ldapDataObj.isRepl() + if replOn: + osLinuxShort = self.clVars.Get("os_linux_shortname") + remoteServer = self.ldapDataObj.getNameRemoteServer(userName, + osLinuxShort) + # Получаем монтируемые директории + names, dictRes = self.getUserMountResources(userName, homeDir, + remoteServer) + # Путь к профилю пользователя по умолчанию + defaultPath = "" + # Хост пользователя по умолчанию + defaultHost = domain + # Флаг - будет использован инкрементальный архив + flagIncrArch = True + # Ошибка при монтировании удаленного сервера + flagErrorMountRemote = False + # Ошибка при cинхронизации с удаленного сервера + flagErrorSyncRemote = False + # Статус синхронизации + syncStatus = True + osLinuxShort = self.clVars.Get("os_linux_shortname") + + # Если профиль на удаленном сервере + if remoteServer and replOn: + # имя файла архива в процессе архивации + archPathProcess = "" + # имя файла архива - после архивации + archPathSuccess = "" + # Создаем инкрементальный архив в случае репликации + prevHost = "" + # Дата профиля на текущем сервере + dateDefaultTemplate = "" + # Текущее время + currTime = str(float(time.time())) + # Создаваемые пути при монтировании + for name in names: + # Пропускаем монтирование директории если нет синхронизации + if not sync and name == "remote_profile": + continue + path = dictRes[name]["path"] + res = dictRes[name]["resource"] + # Создаем пользовательскую директории для профиля + if not os.path.exists(path): + try: + os.mkdir(path) + os.chown(path, uid, gid) + os.chmod(path,0700) + except OSError: + self.printERROR(_("Error creating directory")) + self.printERROR(_("Permission denied: '%s'")%path) + flagError = True + break + #Проверяем на монтирование директории + if os.path.ismount(path): + continue + # Имя хоста для монтирования ресурса + hostConnect = defaultHost + if name == "remote_profile": + hostConnect = remoteServer + # Монтируем Samba ресурс + textLine = self.mountSleepRes(hostConnect, userName, userPwd, + uid, gid, res, path) + if not (textLine is None): + if name == "remote_profile": + flagErrorMountRemote = True + break + else: + self.printERROR(_("Can not mount Samba resource [%s]")\ + %res + " ...") + flagError = True + break + # Если нет синхронизации отменяем обработку + if not sync: + continue + if name == "profile" or name == "remote_profile": + # Находим директорию профиля + homeTemplate = os.path.join(path, osLinuxShort) + if not os.path.exists(homeTemplate): + homeTemplate = os.path.join(path, "." + osLinuxShort) + # Примонтирована директория профиля с текущего сервера + if name == "profile": + # Перенос профиля на сервере в директорию без точки + if not self.upgradeTemplateClient(userName, homeDir, + path): + flagError = True + break + fileConfig = os.path.join(homeTemplate, + self.configFileDesktop) + if os.path.exists(fileConfig): + objConfig = iniParser(fileConfig) + data = self.getDataInConfig("main", ["status_sync", + "date"], + objConfig) + if data: + status = data.get("status_sync") + date = data.get("date") + if date and status=="success": + dateDefaultTemplate = date + # Примонтирована директория профиля с удаленного сервера + # и есть дата профиля текущего сервера + if name == "remote_profile" and dateDefaultTemplate: + # Даем команду на создание инкрементального архива проф. + fileConfig = os.path.join(homeTemplate,self.configFileServer) + varsConfig = {"arch_date":dateDefaultTemplate, + "curr_time":currTime} + self.setServerCommand(["pack"], varsConfig, fileConfig) + # Отмонтируем директорию профиля с удаленного сервера + textLine = self.umountSleepPath(path) + if not textLine: + flagError = True + break + pathTemplateCurr = dictRes["profile"]["path"] + homeTemplateCurr = os.path.join(pathTemplateCurr,osLinuxShort) + # Синхронизация профиля с текущего сервера + hostConnect = defaultHost + if not self.syncLoginTemplate(hostConnect, userName,homeDir, + homeTemplateCurr, uid, gid, + progress): + flagError = True + break + # Монтируем директорию профиля с удаленного сервера + hostConnect = remoteServer + textLine = self.mountSleepRes(hostConnect,userName,userPwd, + uid,gid,res,path) + if not (textLine is None): + self.printERROR(_("Can not mount Samba resource [%s]")\ + %res + " ...") + flagError = True + break + extSuccess = "gz" + extProcess = "process" + strCurrentTime = currTime.replace(".","_") + archPathTmp = os.path.join(path, "profile.%s.%s.tar"\ + %(osLinuxShort, strCurrentTime)) + # имя файла архива в процессе архивации + archPathSuccess = "%s.%s"%(archPathTmp,extSuccess) + # имя файла архива - после архивации + archPathProcess = "%s.%s"%(archPathTmp,extProcess) + # Ищем файл архива + if not self.foundArchFile(strCurrentTime, archPathProcess, + archPathSuccess, progress, + remoteServer): + flagIncrArch = False + break + # Если нет архива применяем rsync синхронизацию + if not os.path.exists(archPathSuccess): + flagIncrArch = False + break + else: + # Распаковываем архив в домашнюю директорию + if not self.unpackTemplate(homeDir, archPathSuccess, + progress, remoteServer): + flagError = True + if archPathSuccess: + # Удаление архивного файла + if os.path.exists(archPathSuccess): + # Удаляем файл архива + os.remove(archPathSuccess) + break + # Cканирование домашней директории и получение списка + # файлов + pathListFile = os.path.join(homeTemplate, + self.listTemplFile) + if not self.removeFilesInTemplate(homeDir, pathListFile): + # Отмонтируем пользовательские ресурсы в + # случае ошибки + self.umountUserRes(homeDir) + return False + else: + if name == "remote_profile": + flagIncrArch = False + if archPathProcess or archPathSuccess: + # Удаление архивных файлов + rmFiles = [archPathProcess, archPathSuccess] + for rmFile in rmFiles: + if os.path.exists(rmFile): + # Удаляем файл архива + os.remove(archPathSuccess) + # Если не удалось замонтировать удаленный профиль + if flagErrorMountRemote: + syncStatus = False + # Если не удалось использовать инкрементальный архив + elif flagIncrArch is False: + # Синхронизируем удаленный профиль + pathTemplate = dictRes["remote_profile"]["path"] + homeTemplate = os.path.join(pathTemplate, osLinuxShort) + if not self.upgradeTemplateClient(userName, homeDir, + pathTemplate): + # Отмонтируем пользовательские ресурсы в случае ошибки + self.umountUserRes(homeDir) + return False + # Синхронизация профиля с удаленного сервера + hostConnect = remoteServer + ret = self.syncLoginTemplate(hostConnect, userName, homeDir, + homeTemplate, uid, gid, progress, + False) + if not ret: + flagErrorSyncRemote = True + syncStatus = False + # Если не удалось синхронизировать удаленный синхронизируем + # текущий + homeTemplate = os.path.join(dictRes["profile"]["path"], + osLinuxShort) + # Синхронизация профиля пользователя с текущего сервера + hostConnect = defaultHost + if not self.syncLoginTemplate(hostConnect,userName,homeDir, + homeTemplate,uid,gid,progress): + # Отмонтируем пользовательские ресурсы в случае ошибки + self.umountUserRes(homeDir) + return False + + # Отмонтируем директорию профиля пользователя на удаленном сервере + if sync and "remote_profile" in dictRes: + umountPath = dictRes["remote_profile"]["path"] + textLine = self.umountSleepPath(umountPath) + if not textLine: + # Отмонтируем пользовательские ресурсы в случае ошибки + self.umountUserRes(homeDir) + return False + # Удаляем директорию удаленного профиля у клиента + if os.path.exists(umountPath): + os.rmdir(umountPath) + + # Если профиль на текущем сервере + elif not remoteServer and replOn or not replOn: + # Создаваемые пути при монтировании + for name in names: + path = dictRes[name]["path"] + res = dictRes[name]["resource"] + # Создаем пользовательскую директории для профиля + if not os.path.exists(path): + try: + os.mkdir(path) + os.chown(path, uid, gid) + os.chmod(path,0700) + except OSError: + self.printERROR(_("Error creating directory")) + self.printERROR(_("Permission denied: '%s'")%path) + flagError = True + break + # Проверяем на монтирование директории + if os.path.ismount(path): + continue + # Имя хоста для монтирования ресурса + hostConnect = defaultHost + textLine = self.mountSleepRes(hostConnect, userName, userPwd, + uid, gid, res, path) + if not (textLine is None): + self.printERROR(_("Can not mount Samba resource [%s]")\ + %res + " ...") + flagError = True + break + # Если нет синхронизации отменяем обработку + if not sync: + continue + if name == "profile": + pathTemplate = path + # Перенос профиля на сервере в директорию без точки + if not self.upgradeTemplateClient(userName, homeDir, + pathTemplate): + flagError = True + break + # Директория профиля + homeTemplate = os.path.join(path, osLinuxShort) + # Синхронизация профиля пользователя с текущего сервера + hostConnect = defaultHost + if not self.syncLoginTemplate(hostConnect, userName, homeDir, + homeTemplate, uid, gid, + progress): + # Отмонтируем пользовательские ресурсы в случае ошибки + self.umountUserRes(homeDir) + return False + + if flagError: + # Отмонтируем пользовательские ресурсы в случае ошибки + self.umountUserRes(homeDir) + return False + + if sync: + logOutFile = os.path.join(homeDir,self.logOutFile) + if syncStatus: + self.setVarToConfig("main", {"status_sync":"success"}, + configFileName, uid, gid) + self.createUserFile(logOutFile,"SUCCESS", uid, gid) + else: + self.createUserFile(logOutFile,"ERROR", uid, gid) + self.setVarToConfig("main", {"status_sync":"error"}, + configFileName, uid, gid) + self.printWARNING(_("Error in sync with the remote server %s")\ + %remoteServer) + self.printSUCCESS(_("Mounted user resource of the domain")) + if sync: + self.printOK(_("Get a user profile from the domain") + " ...") + return True + + def isSessionUser(self, userName): + """Проверка на вход пользователя в X""" + xSession = False + reFoundUser = re.compile("%s\s+:\d+\s+"%(userName)) + resWho = self.execProg("who",False,False) + if resWho and type(resWho) == types.ListType: + for string in resWho: + if reFoundUser.search(string): + xSession = True + break + return xSession + + def moveHomeDir(self, userHome): + """Переносим файлы пользователя в Home/Moved""" + # Находим директории и файлы в домашней директории + pathProg = os.getcwd() + os.chdir(userHome) + dirs = [] + files = [] + movedLink = os.path.join('Moved') + movedPath = os.path.join('Home',"Moved") + filesAndDir = filter(lambda x: not ('.' in x[0] or x in\ + ['Disks','Home','Moved','FTP','Desktop'] or os.path.islink(x)), + os.listdir('.')) + filesDir = [] + for fd in filesAndDir: + if os.path.islink(fd): + os.unlink(fd) + else: + filesDir.append(fd) + # Нахождение файлов отличных ссылок и .* в Desktop + pathDesktop = './Desktop' + filesDirDesk = [] + if os.path.exists(pathDesktop): + filesDirDesk = filter(lambda x: not os.path.islink(x) and\ + not os.path.split(x)[1].startswith('.') and\ + not x.rpartition('.')[2]=='desktop', + map(lambda x: os.path.join(pathDesktop,x),\ + os.listdir(pathDesktop))) + movedPathDesk = os.path.join(movedPath, pathDesktop) + filesDir += filesDirDesk + if not filesDir or not os.path.exists("Home"): + # Удаляем пустую папку Moved + if os.path.exists(movedPath) and not os.listdir(movedPath): + os.rmdir(movedPath) + # Удалям ссылку на Moved в домашней директории + if os.path.islink(movedLink) and not os.path.exists(movedPath): + os.unlink(movedLink) + return True + if not os.path.exists(movedPath): + os.mkdir(movedPath) + directFile = os.path.join(movedPath,".directory") + if not os.path.exists(directFile): + txt = "[Desktop Entry]\nIcon=folder-development" + fd = os.open(directFile, os.O_CREAT) + os.close(fd) + FD = open (directFile, "r+") + FD.write(txt) + FD.close() + if not os.path.exists(movedLink): + os.symlink(movedPath, movedLink) + for fd in filesDir: + execStr = "cp -r '%s' '%s'" %(fd, movedPath) + textLine = self.execProg(execStr) + if not (textLine is None): + self.printERROR(_("Can not exec") + " " + str(execStr) +\ + " ...") + return False + execStr = "rm -rf '%s'" %fd + textLine = self.execProg(execStr) + if not (textLine is None): + self.printERROR(_("Can not exec") + " " + str(execStr) +\ + " ...") + return False + os.chdir(pathProg) + return True + + def removeNoiseFiles(self, userHome): + """Удаление файлов, создающих помехи работе dm""" + noiseFiles = ['.dmrc'] + for nsFile in noiseFiles: + rmFile = os.path.join(userHome, nsFile) + if os.path.exists(rmFile): + os.remove(rmFile) + return True + + def removePrivateFiles(self, userHome): + """Удаление приватных файлов""" + privateFiles = ['.kde4/share/apps/kwallet/kdewallet.kwl', + self.configFileServer] + # файлы в .ssh + sshHome = ".ssh" + sshPath = os.path.join(userHome,sshHome) + if os.path.isdir(sshPath): + # .ssh файлы относительно домашней директории пользователя + privateFiles += map(lambda x:os.path.join(sshHome,x),\ + filter(lambda x:\ + os.path.isfile(os.path.join(sshPath,x)),\ + os.listdir(sshPath))) + for prFile in privateFiles: + rmFile = os.path.join(userHome, prFile) + if os.path.exists(rmFile): + os.remove(rmFile) + return True + + def clearUserKey(self, userName): + """Очищает пользовательский ключ ядра""" + # Ищем ключ в ядре + if getKey(userName): + # Если нашли очищаем + ret = clearKey(userName) + if ret == 0: + return True + else: + self.printERROR(_("Can not clear kernel key from user %s")\ + %userName) + return False + return True + + def umountUserResAndSync(self, userName, progress=False, sync=True): + """Отмонтирование пользовательских ресурсов и синхронизация настроек""" + # Проверяем на root + if not self.isRoot(): + return False + domain = self.clVars.Get("cl_remote_host") + hostAuth = self.clVars.Get("os_remote_auth") + # В случае компьютера вне домена + if not hostAuth or not domain: + self.printSUCCESS(_("To be used local profile.")) + return True + connectDomain = self.isDomain() + if not connectDomain: + # Отмонтируем пользовательские ресурсы в случае ошибки + self.umountUserRes() + return False + # Если пользователь в X сессии тогда не будем отмонтировать ресурсы + if self.isSessionUser(userName): + self.printERROR(_("User %s is in X session")%userName) + self.printERROR(_("Can not unmount user %s resource")%userName) + return False + # Информация о пользователе из LDAP + userLdapInfo = self.ldapDataObj.getUserLdapInfo(userName) + if userLdapInfo: + uid = int(userLdapInfo['uid']) + gid = int(userLdapInfo['uid']) + homeDir = userLdapInfo['home'] + else: + self.printERROR(_("Can not found user %s in LDAP")%userName) + self.umountUserRes() + return False + # Файл хранения настроек пакета + configFileName = os.path.join(homeDir, self.configFileDesktop) + if os.path.exists(homeDir): + self.moveHomeDir(homeDir) + else: + # Отмонтируем пользовательские ресурсы в случае ошибки + self.printERROR(_("Directory %s not found") % homeDir) + self.umountUserRes(homeDir) + return False + # необходима ли синхронизация с локальным сервером + needSync = sync + # проверяем произведен ли корректный вход в систему - + # в этом случае закачиваем профиль с локальной машины на сервер + exitStr = iniParser(configFileName).getVar('main','status_sync') + # проверяем cтатус синхронизации: не содержит ли он "process" + if exitStr == "process" or exitStr == "error": + needSync = False + osLinuxShort = self.clVars.Get("os_linux_shortname") + # Получаем монтируемые директории + names, dictRes = self.getUserMountResources(userName, homeDir, False) + pathUnix = dictRes["profile"]["path"] + homeTemplate = os.path.join(pathUnix, osLinuxShort) + if needSync: + # Синхронизация профиля пользователя на сервер + if not self.syncUser(userName, homeDir, "logout", uid, gid, + homeTemplate): + # Удаляем файлы, мешающие работе dm + self.removeNoiseFiles(homeDir) + # Удаляем приватные файлы + self.removePrivateFiles(homeDir) + # Очищаем ключ в ядре + self.clearUserKey(userName) + # Отмонтируем пользовательские ресурсы в случае ошибки + self.umountUserRes(homeDir) + return False + # Удаляем файлы, мешающие работе dm + self.removeNoiseFiles(homeDir) + # Удаляем приватные файлы + self.removePrivateFiles(homeDir) + # Очищаем ключ в ядре + self.clearUserKey(userName) + # Отмонтируем директории + if not self.umountUserRes(homeDir): + return False + # Удаляем пустые директории + for name in names: + path = dictRes[name]["path"] + if os.path.exists(path) and not os.listdir(path): + try: + os.rmdir(path) + except: + self.printERROR(_("Can not remove dir %s")% path) + return False + if needSync: + self.printSUCCESS(_("Saved a user profile in the domain")) + self.printOK(_("Umounted user resource in domain") + " ...") + return True + + def getMountUserPaths(self, homeDir=False): + """Находит пользовательские примонтированные пути""" + # Имя пользователя + if not homeDir: + userName = self.clVars.Get("ur_login") + try: + homeDir = pwd.getpwnam(userName).pw_dir + except: + homeDir = os.path.join("/home",userName) + dirStart, dirEnd = os.path.split(homeDir) + mountTemplateDir = os.path.join(dirStart, ".%s" %dirEnd) + mountRemoteTemplateDir = os.path.join(dirStart, ".%s.remote" %dirEnd) + return filter(lambda x: x.startswith(homeDir) or\ + x.startswith(mountTemplateDir) or\ + x.startswith(mountRemoteTemplateDir), + map(lambda x: x.split(" ")[1],\ + open("/proc/mounts").readlines())) + + def execProg(self, cmdStrProg, inStr=False, retFull=True, envProg={}): + """Выполняет внешнюю программу + + Параметры: + cmdStrProg внешняя программа + inStr данные передаваемые программе на страндартный вход. + Возвращаемые параметры: + строка которую выведет внешняя программа или False в случае ошибки + """ + env_path = {"PATH":getpathenv()} + env = {} + env.update(os.environ.items() + env_path.items() + envProg.items()) + retCode,programOut = runOsCommand(cmdStrProg,inStr,retFull,env) + if not retCode: + return programOut + return False + + def umountSleepPath(self, path): + """Отмонтирует путь при неудаче задержка потом повтор""" + # Задержки при отмонтированиии директории + sleeps = [0.5, 2, 5] + # Проверяем на монтирование директорию + if os.path.ismount(path): + textLine = self.execProg("umount %s"%path) + if textLine is False: + i = 0 + flagError = False + while (i/dev/null') != 0: + self.printWARNING(_("Error restarting")+" "+dbusDaemon+" ...") + return False + return True + + def getInfoService(self, service, option): + """Получить параметр сервиса из env""" + if not self.optionsInfo: + # файл /var/calculate/remote/server.env + envFile = self.clVars.Get("cl_env_server_path") + optInfo = self.clVars.GetRemoteInfo(envFile) + if optInfo is False: + return False + if optInfo: + self.optionsInfo = optInfo + value = '' + if service in self.optionsInfo and option in self.optionsInfo[service]: + value = self.optionsInfo[service][option] + return value + + + def addDomain(self, domainName): + """Вводим в домен""" + # Проверяем на root + if not self.isRoot(): + return False + netDomain = self.clVars.Get("os_net_domain") + # Получам имя сервера (домена) + if "." in domainName: + domain = domainName + else: + domain = "%s.%s" %(domainName,netDomain) + execStr = "ping -c 2 -i 0.3 %s" %domain + resPing = self.execProg(execStr, False, False) + if not resPing: + self.printERROR(_('Can not execute "%s"')%execStr) + return False + foudHost = False + foudHostSamba = False + foundMountRemote = False + reFoundHost = re.compile("(\d+)\% packet loss") + if resPing and type(resPing) == types.ListType and len(resPing)>=2: + pingStr = resPing[-2].strip() + reSearch = reFoundHost.search(pingStr) + if reSearch and reSearch.group(1) == "0": + foudHost = True + if not foudHost: + self.printERROR(_("Not found domain %s")%domain) + return False + reFoundHostSamba = re.compile("Server=\[Samba.+\]") + resSmbClient = self.execProg("smbclient -N -L %s" %domain, + False,False) + if resSmbClient and type(resSmbClient) == types.ListType: + for string in resSmbClient: + if reFoundHostSamba.search(string): + foudHostSamba = True + break + if not foudHostSamba: + self.printERROR(_("Not found Samba server in %s")%domain) + return False + pwd = False + if self.clVars.Get("cl_remote_host") and \ + self.clVars.Get("cl_remote_host") != domain: + if not self.delDomain(): + return False + elif self.clVars.Get("cl_remote_host") and \ + self.clVars.Get("cl_remote_host") == domain: + pwd = self.clVars.Get("cl_remote_pw") + foundMountRemote = os.path.ismount("/var/calculate/remote") + foundMountHome = os.path.ismount("/home") + if foundMountRemote: + self.printWARNING(_("Samba resource [%s] is mount")%\ + "remote" + " ...") + else: + if pwd: + userPwd = pwd + else: + userPwd=self.getUserPassword(\ + _("Domain password for the desktop")) + pathRemote = "/var/calculate/remote" + pwdRemote = userPwd + if not os.path.exists(pathRemote): + os.makedirs(pathRemote) + mountStr = "mount -t cifs -o user=client //%s/remote %s"\ + %(domain,pathRemote) + textLine = self.execProg(mountStr, None, True, {"PASSWD":pwdRemote}) + if not (textLine is None): + self.printERROR(_("Can not mount Samba resource [%s]")%\ + "remote" + " ...") + return False + else: + self.printSUCCESS(_("Mount Samba resource [%s]")%"remote" + \ + " ...") + self.clVars.Write("cl_remote_host", domain, True, "local") + self.clVars.Write("cl_remote_pw", userPwd, True, "local") + pathHome = "/var/calculate/client-home" + if foundMountHome: + self.printWARNING(str(pathHome)+ " " +_("is mount")+ + " ...") + else: + if not os.path.exists(pathHome): + os.makedirs(pathHome) + mountStr = "mount -o bind %s /home" %pathHome + textLine = self.execProg(mountStr) + if not (textLine is None): + self.printERROR(_("Can not mount") + " " + str(pathHome) +\ + " ...") + return False + self.printSUCCESS(_("Mount") + " " + str(pathHome) + " " +\ + " ...") + #servDn = self.clVars.Get("ld_services_dn") + servDn = self.getInfoService("ldap", "services_dn") + #unixDn = self.clVars.Get("ld_unix_dn") + unixDn = self.getInfoService("unix", "dn") + # bindDn = self.clVars.Get("ld_bind_dn") + bindDn = self.getInfoService("unix", "bind_dn") + #bindPw = self.clVars.Get("ld_bind_pw") + bindPw = self.getInfoService("unix", "bind_pw") + # запишем их + if not (servDn and unixDn and bindDn and bindPw): + self.printERROR(_("Not found server info") + ": " +\ + _("services DN or unix DN or bind DN or bind password")) + return False + # Наложим шаблоны - install, domain + # Действие - инсталяция + self.clVars.Set("cl_pass_action", "install", True) + # Шаг - ввод в домен + self.clVars.Set("cl_pass_step", "domain", True) + # Доменная авторизация + self.clVars.Set("os_remote_auth", domain) + if not self.applyTemplatesFromSystem(): + self.printERROR(_("Can not apply install or domain templates")) + return False + # Рестартуем dbus + self.restartDBus() + if not self.addDaemonAutostart("client"): + return False + # Записываем переменную + self.clVars.Write("os_remote_auth", domain, True) + # Записываем текущую версию программы + currentVersion = self.clVars.Get("cl_ver") + self.clVars.Write("os_remote_client", currentVersion, True) + self.printOK(_("Computer added to domain %s")%domain + " ...") + return True + + def relevanceTemplates(self, hostAuth): + """Определяем актуальность наложенных шаблонов + + в зависимости от версии программы + Перед запуском обязательно должен быть определен объект переменных + self.clVars + Если актуальны - True, если нет False + """ + # '' или имя хоста + if hostAuth != self.clVars.Get("os_remote_auth"): + return False + clTempl = template(self.clVars) + # Текущая версия программы + currentVersion = self.clVars.Get("cl_ver") + # Версия программы которая ранее работала с шаблонами + previousVersion = self.clVars.Get("os_remote_client") + cVersion,pVersion = clTempl._convertVers(currentVersion,previousVersion) + # Если версии программ не равны то шаблоны не актуальные + if cVersion != pVersion: + return False + return True + + def applyRelevanceTemplates(self, hostAuth=""): + """Накладывает релевантные шаблоны + + Перед запуском обязательно должен быть определен объект переменных + self.clVars + """ + if not self.relevanceTemplates(hostAuth): + if hostAuth: + # Устанавливаем шаг вход в домен + self.clVars.Set("cl_pass_step","domain",True) + else: + # Устанавливаем шаг выход из домена + self.clVars.Set("cl_pass_step","undomain",True) + self.clVars.Set("os_remote_auth", hostAuth) + # Наложим шаблоны + dirsAndFiles = self.applyTemplatesFromSystem() + if not dirsAndFiles: + if hostAuth: + self.printERROR(_("Can not apply domain templates")) + else: + self.printERROR(_("Can not apply undomain templates")) + return False + if hostAuth: + self.printOK(_("Set templates of network mode")) + currentVersion = self.clVars.Get("cl_ver") + self.clVars.Write("os_remote_client", currentVersion, True) + self.clVars.Write("os_remote_auth", hostAuth, True) + else: + self.printOK(_("Set templates of local mode")) + self.clVars.Delete("os_remote_auth") + self.clVars.Delete("os_remote_client") + return True + + def mountRemote(self): + """Монтирование remote и домашней директории если компьютер в домене + + а так-же ввод в домен если найдено имя хоста и пароль для подключения + """ + # Проверяем на root + if not self.isRoot(): + return False + domain = self.clVars.Get("cl_remote_host") + if domain: + foundMountRemote = os.path.ismount("/var/calculate/remote") + foundMountHome = os.pathismount("/home") + else: + self.printWARNING(_("This computer is not in domain")) + # Если шаблоны не актуальны накладываем новую версию шаблонов + if not self.applyRelevanceTemplates(): + return False + return True + pathHome = "/var/calculate/client-home" + if foundMountRemote: + self.printWARNING(_("Samba resource [%s] is mount")%"remote" + \ + " ...") + if foundMountHome: + self.printWARNING(str(pathHome) + " " +_("is mount")+ + " ...") + if foundMountHome and foundMountRemote: + # Накладываем сетевые шаблоны + if domain: + self.clVars.flIniFile() + if not self.applyRelevanceTemplates(domain): + return False + return True + flagLocalTemplate = False + if not foundMountRemote: + pathRemote = "/var/calculate/remote" + pwdRemote = self.clVars.Get("cl_remote_pw") + if not (domain and pwdRemote): + self.printERROR(_("Not found variable")+\ + ": cl_remote_pw ...") + return False + if not os.path.exists(pathRemote): + os.makedirs(pathRemote) + mountStr = "mount -t cifs -o user=client //%s/remote %s"\ + %(domain,pathRemote) + textLine = self.execProg(mountStr, None, True, {"PASSWD":pwdRemote}) + if not (textLine is None): + self.printWARNING(_("Can not mount Samba resource [%s]")%\ + "remote" + " ...") + flagLocalTemplate = True + # Если шаблоны не актуальны накладываем новую версию шаблонов + if not self.applyRelevanceTemplates(): + return False + if foundMountHome: + umountStr = "umount /home" + textLine = self.execProg(umountStr) + if not (textLine is None): + self.printERROR(_("Can not unmount") + " /home") + return False + return True + self.printSUCCESS(_("Mount Samba resource [%s]") % "remote" +\ + " ...") + if not foundMountHome and not flagLocalTemplate: + if not os.path.exists(pathHome): + os.makedirs(pathHome) + mountStr = "mount -o bind %s /home" %pathHome + textLine = self.execProg(mountStr) + if not (textLine is None): + self.printERROR(_("Can not mount") + " " + str(pathHome) +\ + " ...") + return False + self.printSUCCESS(_("Mount") + " " + str(pathHome) + " " +\ + " ...") + # Накладываем сетевые шаблоны + if domain: + self.clVars.flIniFile() + if not self.applyRelevanceTemplates(domain): + return False + self.restartDBus() + return True diff --git a/pym/cl_client_cmd.py b/pym/cl_client_cmd.py new file mode 100644 index 0000000..5664877 --- /dev/null +++ b/pym/cl_client_cmd.py @@ -0,0 +1,140 @@ +#-*- coding: utf-8 -*- + +# Copyright 2010 Mir Calculate Ltd. http://www.calculate-linux.org +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from cl_client import client, __app__, __version__ +from cl_opt import opt, TitledHelpFormatter +import sys +from cl_share_cmd import share_cmd + +# Перевод сообщений для программы +from cl_lang import lang +lang().setLanguage(sys.modules[__name__]) + +# Использование программы +USAGE = _("%prog [options] domain") + +# Коментарии к использованию программы +COMMENT_EXAMPLES = _("Adds settings for connecting to domain \ +server.calculate.ru") + +# Пример использования программы +EXAMPLES = _("%prog server.calculate.ru") + +# Описание программы (что делает программа) +DESCRIPTION = _("Changes settings for connecting to domain") + +# Опции командной строки +CMD_OPTIONS = [{'shortOption':"r", + 'help':_("remove the settings for connecting to a domain")}, + {'longOption':"install", + 'help':_("add use of scripts this package for window manager")}, + {'longOption':"uninstall", + 'help':_("remove use of scripts this package for window \ + manager and, if necessary, removes from domain")}, + {'longOption':"mount", + 'help':_("mount [remote] resource for domain")}] + +class client_cmd(share_cmd): + def __init__(self): + # Объект опций командной строки + self.optobj = opt(\ + package=__app__, + version=__version__, + usage=USAGE, + examples=EXAMPLES, + comment_examples=COMMENT_EXAMPLES, + description=DESCRIPTION, + option_list=CMD_OPTIONS + opt.variable_control+opt.color_control, + formatter=TitledHelpFormatter(), + check_values=self.checkOpts) + # Создаем объект логики + self.logicObj = client() + # Создаем переменные + self.logicObj.createClVars() + # Названия несовместимых опций + self.optionsNamesIncompatible = ["r", "install", "uninstall", "mount"] + # Названия опций несовмесимых с именем домена + self.optionsNamesNotDomain = self.optionsNamesIncompatible + + def getOptionsNotDomain(self, optObj): + """Получаем опции несовместимые с именем домена""" + retList = [] + for nameOpt in self.optionsNamesNotDomain: + retList.append(getattr(optObj, nameOpt)) + return retList + + def _getNamesAllSetOptions(self): + """Выдает словарь измененных опций""" + setOptDict = self.optobj.values.__dict__.items() + defaultOptDict = self.optobj.get_default_values().__dict__.items() + return dict(set(setOptDict) - set(defaultOptDict)).keys() + + def getStringIncompatibleOptions(self): + """Форматированная строка несовместимых опций разделенных ','""" + listOpt = list(set(self.optionsNamesIncompatible) &\ + set(self._getNamesAllSetOptions())) + return ", ".join(map(lambda x: len(x) == 1 and "'-%s'"%x or "'--%s'"%x,\ + listOpt)) + + def checkOpts(self, optObj, args): + """Проверка опций командной строки""" + optionsNotDomain = self.getOptionsNotDomain(optObj) + if not args: + options = optionsNotDomain + [optObj.color, optObj.vars] + if not filter(lambda x: x, options): + errMsg = _("no such argument")+":"+" %s" %USAGE.split(" ")[-1] + self.optobj.error(errMsg) + return False + elif len(filter(lambda x: x, optionsNotDomain))>1: + errMsg = _("incompatible options")+":"+" %s"\ + %self.getStringIncompatibleOptions() + self.optobj.error(errMsg) + return False + elif filter(lambda x: x, optionsNotDomain): + errMsg = _("unnecessary argument")+":"+" %s" %USAGE.split(" ")[-1] + self.optobj.error(errMsg) + return False + if len(args)>1: + errMsg = _("incorrect argument") + ":" + " %s" %" ".join(args) + self.optobj.error(errMsg) + return False + return optObj, args + + def addDomain(self, domainName): + """Ввод в домен""" + if domainName: + return self.logicObj.addDomain(domainName) + else: + self.printERROR(_('Not found argument domain name')) + return False + + def delDomain(self): + """Вывод из домена""" + return self.logicObj.delDomain() + + def install(self): + """Инсталяция""" + return self.logicObj.installClient() + + def uninstall(self): + """Удаление""" + return self.logicObj.uninstallClient() + + def mountRemote(self): + """Монтирование remote и домашней директории если компьютер в домене + + а так-же ввод в домен если найдено имя хоста и пароль для подключения + """ + return self.logicObj.mountRemote() \ No newline at end of file diff --git a/pym/cl_fill_client.py b/pym/cl_fill_client.py new file mode 100644 index 0000000..34ddde4 --- /dev/null +++ b/pym/cl_fill_client.py @@ -0,0 +1,19 @@ +#-*- coding: utf-8 -*- + +# Copyright 2010 Mir Calculate Ltd. http://www.calculate-linux.org +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +class fillVars(): + pass diff --git a/pym/cl_share_cmd.py b/pym/cl_share_cmd.py new file mode 100644 index 0000000..cec0b1d --- /dev/null +++ b/pym/cl_share_cmd.py @@ -0,0 +1,64 @@ +#-*- coding: utf-8 -*- + +# Copyright 2010 Mir Calculate Ltd. http://www.calculate-linux.org +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import sys + +from cl_print import color_print +from cl_utils import _error + +# Перевод сообщений для программы +from cl_lang import lang +lang().setLanguage(sys.modules[__name__]) + +class share_cmd(color_print, _error): + """Класс общих методов обработки опций командной строки""" + def printVars(self, optObj): + """Печать переменных""" + if optObj.vars: + terms = optObj.vars.split(",") + self.logicObj.printVars(terms) + + def setVars(self, optObj): + """Установка переменных""" + if optObj.set: + for vals in optObj.set: + for val in vals.split(','): + k,o,v = val.partition('=') + if self.logicObj.clVars.exists(k): + if not self.logicObj.clVars.SetWriteVar(k,v): + return False + else: + self.printERROR(_('variable %s not found')%k) + return False + return True + + def writeVars(self, optObj): + """Запись переменных""" + if optObj.set: + if not self.logicObj.clVars.WriteVars(): + errMsg = self.getError() + if errMsg: + self.printERROR(errMsg.strip()) + self.printERROR(_('Can not write template variables')) + return False + return True + + def setPrintNoColor(self, optObj): + """Установка печати сообщений без цвета""" + if optObj.color and optObj.color=="never": + color_print.colorPrint = lambda *arg : sys.stdout.write(arg[-1]) or\ + sys.stdout.flush() + diff --git a/pym/cl_sync.py b/pym/cl_sync.py new file mode 100644 index 0000000..2dca863 --- /dev/null +++ b/pym/cl_sync.py @@ -0,0 +1,132 @@ +#-*- coding: utf-8 -*- + +# Copyright 2010 Mir Calculate Ltd. http://www.calculate-linux.org +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from cl_client import client, __app__, __version__ +from cl_opt import opt, TitledHelpFormatter +import sys +from cl_share_cmd import share_cmd + +# Перевод сообщений для программы +from cl_lang import lang +lang().setLanguage(sys.modules[__name__]) + +# Использование программы +USAGE = _("%prog [options] user") + +# Коментарии к использованию программы +COMMENT_EXAMPLES = _("Mount resources and synchronize user profile") + +# Пример использования программы +EXAMPLES = _("%prog user_name") + +# Описание программы (что делает программа) +DESCRIPTION = _("Mounting resources and synchronize the user profile") + +# Опции командной строки +CMD_OPTIONS = [{'longOption':"progress", + 'help':_("show progress bar for xdm startup")}, + {'longOption':"login", + 'help':_("mount user resource and synchronize the user \ +profile")}, + {'longOption':"logout", + 'help':_("synchronize the user profile and umount user \ +resource")}, + {'longOption':"nosync", + 'help':_("not synchronize the user preferences, is used \ +in conjunction with the 'login' or 'logout'")}] + +class sync(share_cmd): + def __init__(self): + # Объект опций командной строки + self.optobj = opt(\ + package=__app__, + version=__version__, + usage=USAGE, + examples=EXAMPLES, + comment_examples=COMMENT_EXAMPLES, + description=DESCRIPTION, + option_list=CMD_OPTIONS + opt.variable_control+opt.color_control, + formatter=TitledHelpFormatter(), + check_values=self.checkOpts) + # Создаем объект логики + self.logicObj = client() + # Создаем переменные + self.logicObj.createClVars() + # Названия несовместимых опций + self.optionsNamesIncompatible = ["login", "logout"] + # Названия обязательных опций + self.optionsNamesRequired = self.optionsNamesIncompatible + + def getOptionsRequired(self, optObj): + """Получаем обязательные опции""" + retList = [] + for nameOpt in self.optionsNamesRequired: + retList.append(getattr(optObj, nameOpt)) + return retList + + def _getNamesAllSetOptions(self): + """Выдает словарь измененных опций""" + setOptDict = self.optobj.values.__dict__.items() + defaultOptDict = self.optobj.get_default_values().__dict__.items() + return dict(set(setOptDict) - set(defaultOptDict)).keys() + + def getStringIncompatibleOptions(self): + """Форматированная строка несовместимых опций разделенных ','""" + listOpt = list(set(self.optionsNamesIncompatible) &\ + set(self._getNamesAllSetOptions())) + return ", ".join(map(lambda x: len(x) == 1 and "'-%s'"%x or "'--%s'"%x,\ + listOpt)) + + def checkOpts(self, optObj, args): + """Проверка опций командной строки""" + optionsRequired = self.getOptionsRequired(optObj) + if not args: + errMsg = _("no such argument")+":"+" %s" %USAGE.split(" ")[-1] + self.optobj.error(errMsg) + return False + elif len(filter(lambda x: x, optionsRequired))>1: + errMsg = _("incompatible options")+":"+" %s"\ + %self.getStringIncompatibleOptions() + self.optobj.error(errMsg) + return False + elif not filter(lambda x: x, optionsRequired): + errMsg = _("required option")+":"+" %s"\ + %" or ".join(map(lambda x:\ + len(x) == 1 and "'-%s'"%x or "'--%s'"%x,\ + self.optionsNamesRequired)) + self.optobj.error(errMsg) + return False + if len(args)>1: + errMsg = _("incorrect argument") + ":" + " %s" %" ".join(args) + self.optobj.error(errMsg) + return False + return optObj, args + + def setUserName(self, userName): + """Установка имени пользователя""" + self.logicObj.clVars.Set("ur_login", userName, True) + + + def mountUserResAndSync(self, sync=True, progress=False): + """Монтирование ресурсов и синхронизация при входе""" + userName = self.logicObj.clVars.Get("ur_login") + return self.logicObj.mountUserResAndSync(userName, sync=sync, + progress=progress) + + def umountUserResAndSync(self, sync=True, progress=False): + """Отмонтирование ресурсов и синхронизация при выходе""" + userName = self.logicObj.clVars.Get("ur_login") + return self.logicObj.umountUserResAndSync(userName, sync=sync, + progress=progress) \ No newline at end of file diff --git a/pym/cl_vars_client.py b/pym/cl_vars_client.py new file mode 100644 index 0000000..e62ce48 --- /dev/null +++ b/pym/cl_vars_client.py @@ -0,0 +1,64 @@ +#-*- coding: utf-8 -*- + +# Copyright 2010 Mir Calculate Ltd. http://www.calculate-linux.org +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +#Допустимые ключи +# mode - режим переменной r-не переназначается из командной строки, +# w-переназначается из командной строки +# type - тип переменной состоит из двух элементов(что это и для чего +# это) +# value - значение переменной по умолчанию +# official - флаг того, что данная переменная служебная и не отображается +# при печати списка значений переменных + +from cl_client import __version__ +from cl_client import __app__ + +class Data: + # имя программы + cl_name = {'value':__app__} + + # версия программы + cl_ver = {'value':__version__} + + # ip или имя домена (под управлением calculate-server) + cl_remote_host = {'mode':'r'} + + #Логин пользователя + ur_login = {'mode':"r"} + + #Название группы пользователя + ur_group = {'mode':"r"} + + #Полное имя пользователя + ur_fullname = {'mode':"r"} + + # Jabber ID пользователя + ur_jid = {'mode':"r"} + + # Почтовый адрес пользователя + ur_mail = {'mode':"r"} + + # Режим работы клиента + # (local или имя хоста клиента) + os_remote_auth = {'mode':'w'} + + # Версия программы которой были наложены шаблоны + os_remote_client = {'mode':'w'} + + #Пароль пользователя client + cl_remote_pw = {'mode':'w'} + + diff --git a/scripts/cl-client-2.2 b/scripts/cl-client-2.2 new file mode 100644 index 0000000..a20895a --- /dev/null +++ b/scripts/cl-client-2.2 @@ -0,0 +1,70 @@ +#!/usr/bin/python +#-*- coding: utf-8 -*- + +# Copyright 2010 Mir Calculate Ltd. http://www.calculate-linux.org +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import sys +import os +sys.path.insert(0,os.path.abspath('/usr/lib/calculate-2.2/calculate-lib/pym')) +sys.path.insert(0,\ + os.path.abspath('/usr/lib/calculate-2.2/calculate-client/pym')) + +from cl_client_cmd import client_cmd + +from cl_lang import lang +tr = lang() +tr.setGlobalDomain('cl_client') +tr.setLanguage(sys.modules[__name__]) + +if __name__ == "__main__": + obj = client_cmd() + ret = obj.optobj.parse_args() + if ret is False: + sys.exit(1) + opts, args = ret + # Установка цвета при печати сообщений + obj.setPrintNoColor(opts) + # Установка переменных + if not obj.setVars(opts): + sys.exit(1) + # Печать переменных + obj.printVars(opts) + # Если нет печати переменных выполняем логику программы + if not opts.vars: + if args: + domainName = args[0] + # Ввод в домен + if not obj.addDomain(domainName): + sys.exit(1) + elif opts.r: + # Вывод из домена + if not obj.delDomain(): + sys.exit(1) + elif opts.install: + # Инсталяция + if not obj.install(): + sys.exit(1) + elif opts.uninstall: + # Удаление + if not obj.uninstall(): + sys.exit(1) + elif opts.mount: + # Монтирование remote + if not obj.mountRemote(): + sys.exit(1) + # Запись переменных + if not obj.writeVars(opts): + sys.exit(1) + sys.exit(0) diff --git a/scripts/cl-sync-2.2 b/scripts/cl-sync-2.2 new file mode 100644 index 0000000..bdf8c41 --- /dev/null +++ b/scripts/cl-sync-2.2 @@ -0,0 +1,63 @@ +#!/usr/bin/python +#-*- coding: utf-8 -*- + +# Copyright 2010 Mir Calculate Ltd. http://www.calculate-linux.org +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import sys +import os +sys.path.insert(0,os.path.abspath('/usr/lib/calculate-2.2/calculate-lib/pym')) +sys.path.insert(0,\ + os.path.abspath('/usr/lib/calculate-2.2/calculate-client/pym')) + +from cl_sync import sync + +from cl_lang import lang +tr = lang() +tr.setGlobalDomain('cl_desktop') +tr.setLanguage(sys.modules[__name__]) + +if __name__ == "__main__": + obj = sync() + ret = obj.optobj.parse_args() + if ret is False: + sys.exit(1) + opts, args = ret + userName = args[0] + # Установка цвета при печати сообщений + obj.setPrintNoColor(opts) + # Установка имени пользователя + obj.setUserName(userName) + # Установка переменных + if not obj.setVars(opts): + sys.exit(1) + # Печать переменных + obj.printVars(opts) + # Если нет печати переменных выполняем логику программы + if not opts.vars: + sync = not opts.nosync + if opts.login: + # Монтирование ресурсов и синхронизация при входе + if not obj.mountUserResAndSync(sync=sync, + progress=opts.progress): + sys.exit(1) + elif opts.logout: + # Отмонтирование ресурсов и синхронизация при выходе + if not obj.umountUserResAndSync(sync=sync, + progress=opts.progress): + sys.exit(1) + # Запись переменных + if not obj.writeVars(opts): + sys.exit(1) + sys.exit(0) diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..00b055c --- /dev/null +++ b/setup.cfg @@ -0,0 +1,5 @@ +[install] +install-scripts=/usr/bin +install-purelib=/usr/lib/calculate-2.2 +install-platlib=/usr/lib/calculate-2.2 +install-data=/usr/share/calculate-2.2/templates \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100755 index 0000000..bd100e9 --- /dev/null +++ b/setup.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# setup.py --- Setup script for calculate-client + +#Copyright 2010 Calculate Pack, http://www.calculate-linux.org +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import stat +from distutils.core import setup, Extension +from distutils.command.install_data import install_data + +__version__ = "2.2.0" +__app__ = "calculate-client" + + +data_files = [] + +var_data_files = [] + +data_dirs_template = ['client'] +data_dirs_share = ['i18n'] +share_calculate_dir = "/usr/share/calculate-2.2/" +template_calculate_dir = os.path.join(share_calculate_dir, "templates") + + +def __scanDir(scanDir, prefix, dirData, flagDir=False): + """Scan directory""" + files = [] + dirs = [] + if flagDir or stat.S_ISDIR(os.stat(scanDir)[stat.ST_MODE]): + for fileOrDir in os.listdir(scanDir): + absPath = os.path.join(scanDir,fileOrDir) + statInfo = os.stat(absPath)[stat.ST_MODE] + if stat.S_ISREG(statInfo): + files.append(absPath) + elif stat.S_ISDIR(statInfo): + dirs.append(absPath) + if prefix: + scanDir = os.path.join(prefix,scanDir) + dirData.append((scanDir, files)) + for sDir in dirs: + __scanDir(sDir, prefix, dirData, True) + return dirData + +def create_data_files(data_dirs, prefix=""): + """Create data_files""" + data_files = [] + for data_dir in data_dirs: + data = [] + data_files += __scanDir(data_dir, prefix, data) + return data_files + + +data_files += create_data_files (data_dirs_template, template_calculate_dir) +data_files += create_data_files (data_dirs_share, share_calculate_dir) +data_files += [('/etc/init.d', ['data/client'])] +data_files += [('/etc/calculate/xdm/login.d', ['data/login.d/10client'])] +\ + [('/etc/calculate/xdm/logout.d', [])] +\ + [('/var/calculate/templates', [])] + + +class cl_install_data(install_data): + def run (self): + install_data.run(self) + data_file = [("/etc/init.d/client",0755)] + fileNames = map(lambda x: os.path.split(x[0])[1], data_file) + listNames = map(lambda x: filter(lambda y: y, x[0].split("/")),data_file) + data_find = {} + for i in range(len(fileNames)): + listNames[i].reverse() + data_find[fileNames[i]] =[listNames[i],data_file[i][1]] + + for path in self.get_outputs(): + nameFile = os.path.split(path)[1] + if nameFile in data_find.keys(): + data = data_find[nameFile][0] + mode = data_find[nameFile][1] + flagFound = True + iMax = len(data) + pathFile = path + for i in range(iMax): + if data[i] != os.path.split(pathFile)[1]: + flagFound = False + break + pathFile = os.path.split(pathFile)[0] + if flagFound: + os.chmod(path, mode) + + +setup( + name = __app__, + version = __version__, + description = "Mounting resources and synchronize the user profile", + author = "Mir Calculate Ltd.", + author_email = "support@calculate.ru", + url = "http://calculate-linux.org", + license = "http://www.apache.org/licenses/LICENSE-2.0", + package_dir = {'calculate-client': "."}, + packages = ['calculate-client.pym'], + data_files = data_files, + scripts=["./scripts/cl-sync-2.2", + "./scripts/cl-client-2.2"], + ext_modules = [Extension('calculate-client.pym._cl_keys', + library_dirs = ['/usr/lib'], + libraries = ['keyutils'], + sources = ['./lib/cl_keys.i', './lib/cl_keys.c'])], + cmdclass={'install_data': cl_install_data}, +)