|
LWN.net Weekly Edition for March 18, 2004The GPL and library code The GNU General Public License (GPL) is an unforgiving beast; if you distribute something derived from GPL-licensed code, the whole derived product must be distributable under the GPL's terms. This provision effectively prevents the use of GPL-licensed in proprietary, closed-source products. That is an inconvenience for proprietary software vendors, but is clearly what the authors of the GPL intended.In fact, the terms of the GPL work very well for some software vendors as well. Consider this press release from MySQL AB, Sleepycat Software, and Trolltech AS. These companies claim that their 2003 software licensing revenues were up 65% over the previous year. Not bad for companies which make their software available for free. Each of these companies is a provider of "library" code - tools which can built into an application to give it new capabilities. MySQL and Trolltech make their offerings available under the GPL; Sleepycat has its own license which requires source availability (though in a weaker form than the GPL). In each case, however, there is a twist: for a fee, the company will make the same software available under a license which allows closed-source distribution. When this model works, it works well. The free software community gets access to high-quality software, and the company gets the benefits of the free development process. At the same time, the company is able to extract money from others who are making money with the code. This model will only work in some situations; the software in question must be attractive as a component of a larger application, and external contributors must be willing to transfer copyrights or otherwise allow their work to be distributed in closed-source form. But, when those conditions apply, the dual-licensing model appears to work well. There is one interesting problem which occasionally comes up, however; licensing this sort of library code under the GPL can block its use with other software which is available under a free, but GPL-incompatible license. This conflict has been highlighted by the fact that the GPL-incompatible PHP license means that PHP and MySQL 4 cannot be used together (or, more correctly, an application combining the two cannot be redistributed). Since MySQL and PHP are a popular combination, this restriction hurts a lot of people; it also led to a number of distributors sticking with the older MySQL 3 release, which did not have this problem. The GPL-incompatibility of the new XFree86 license is another high-profile example; in that case, the license conflict may be the final straw that signals the end of XFree86 as a viable project. MySQL AB has now acted to mitigate the problem of free but GPL-incompatible licenses; the company has extended the MySQL client library license with the "MySQL FOSS License Exception." This exception provides a series of licenses which can be applied to parts of derived works involving the MySQL client libraries; it includes the PHP license and several others. With this extension, the PHP license conflict is no more. The stated intent of the GPL is to ensure that all derived products remain free software. This extension of the license is clearly compatible with that goal; it still does not allow the covered code to be distributed in a non-free manner. If this sort of exception is adopted more widely, it may point toward a need for a new form of the GPL. If the end result is more free software, that would be a good thing.
SCO Update Last week, we looked at SCO's stock price as a sort of public referendum on the company's prospects. Shortly thereafter, the SCO Group made it clear that company management, too, is watching the stock price closely, and is not pleased with what it is seeing. Thus, SCO has announced a stock buyback program in the hopes of raising the price somewhat - or, at least, halting its decline.What the company has announced is that the board of directors has given its OK for management, "at its discretion," to buy up to 1.5 million shares of SCO stock over the next two years. Board chairman Ralph Yarro is quoted as saying:
At current prices, we believe our stock represents an attractive
investment opportunity and that this action reflects our ongoing
commitment to improving long term stockholder value. We believe we
will have sufficient capital resources to undertake this buyback
program and continue to pursue our strategic initiatives.
The interesting thing, of course, is that capital resources is one thing the SCO group lacks. From the latest quarterly report filed with the SEC, we read that "Our cash and equivalents balance decreased from $64,428,000 as of October 31, 2003 to $57,945,000 as of January 31, 2004." $58 million is not a small cash pile, but one should bear in mind that this pile has to sustain the company in litigation for over a year until the IBM case comes to trial. Delays in that trial seem likely; if SCO should somehow win some sort of judgment, an appeal also seems likely. SCO's ability to stay afloat long enough to see its various lawsuits through is doubtful as it is, without spending millions of dollars on stock buybacks. Company management understands this; that is why the same quarterly report includes this text:
If we repurchase a substantial number of shares during this
24-month period, and we do not generate off-setting revenue form
our UNIX and SCOsource businesses, our cash position could decrease
significantly and our ability to fund future operations could be
adversely impacted.
Spending SCO's scarce cash on SCO stock would thus seem an absurd thing to do. So one might well wonder what is really going on. If one were given to wild speculation, one might come up with either of the following scenarios:
The first scenario looks like a "go directly to jail, do not pass 'Go'" card for the people involved. One never knows, but looting the company in that way looks extreme even for SCO. The second option (issue a PR, do nothing), on the other hand, is something we've seen from this company before. We will find out for sure in future SEC filings, but the odds are that SCO will not be buying back those 1.5 million shares. Meanwhile, the public confirmation from BayStar that Microsoft did, indeed, direct them toward investing in SCO has had its own effect on how the whole SCO case is seen by the wider public. SCO has, at this point, definitively lost the public relations battle. Finally, a related development is the announcement of the launch of Open Source Risk Management and its "open source risk protection services." OSRM will sell you an indemnification policy for free software, and will even allow customers to modify that software. The company's offering is based on "sophisticated code-scanning technology and a set of best practice protocols," along with the results of Groklaw's efforts to track down the origins of the code in the Linux kernel. We can only welcome a company which is trying to make free software users sleep better at night, but it should be noted that this sort of insurance policy needs a risk to insure against. As SCO goes down in flames, potential customers might well wonder if they really need this sort of protection. Let's hope that some other hungry, litigious corporation does not answer that question for them.
MandrakeSoft springs back When MandrakeSoft filed for the "declaration de cessation des paiements" (similar to Chapter 11 bankruptcy in the U.S.) on January 13, 2003, there was some concern about the future of MandrakeSoft and the Mandrake Linux distribution. A little more than a year later, the company and the distribution seem to be doing well. MandrakeSoft recently filed its "redressement judiciaire" plan to emerge from bankruptcy with the French courts, and its stock has already resumed trading on the Marché Libre. This seemed like a good time to ask MandrakeSoft co-founder Gaël Duval for an update on the company's health and its plans for the future.The bankruptcy exit plan has not yet been approved, but Duval said that the company expects the plan will be approved before the end of March. The plan calls for MandrakeSoft to repay €4.1 million over 9 years from revenues, rather than borrowing the money to repay the debt. If the plan is approved before April 15, MandrakeSoft also stands to sell an additional 358,000 shares at €2.10 apiece, according to their shareholder newsletter. What led up to the bankruptcy? Duval said that the main problem was that the company's expenses were too high, as opposed to unsuccessful products. He did single out MandrakeSoft's e-learning venture as an "unprofitable venture." What has the company done to improve its financial picture?
Since 2002 we worked hard to reduce expenses. This included closing some
offices, be more careful about where money goes to, and unfortunately,
reduce the number of employees...We had to re-center Mandrakesoft's
strategy in line with its initial philosophy, which is building easy-to-use
and friendly Linux products and making a business from these products.
After the layoffs, MandrakeSoft is now down to about 60 employees. There are still quite a few people backing the Mandrake Linux distribution, however. Duval noted that there are about 800 registered contributors for the Mandrake Linux Cooker, about 600 for the Cooker-i18n, and approximately 150 for Cooker-AMD64. Duval said that the company has focused on products with better revenue potential, with an increased focus on sales directly through MandrakeSoft's online store rather than sales through distributors that take a larger cut of the profits. The company has also looked to the MandrakeClub, which now has nearly 20,000 subscribers. Duval also noted that MandrakeSoft, like other Linux distributors, saw a marked decline in sales of boxed product as high-speed Internet connections became more common. MandrakeSoft has also been working on "OEM activities," with companies like HP. HP has been offering Mandrake Linux on PCs for some time, and the company recently rolled out new PC models with Mandrake Linux. Duval didn't provide specifics on the deal with HP, but said that it provides a "good income" for the company. For the first time since its 1998/1999 fiscal year, the company can claim a "good income." MandrakeSoft's revenues have increased by 8.4 percent since the first quarter of the last fiscal year. The total revenues for the first quarter total €1,421,000 , with a net profit of €271,000. MandrakeSoft's results might have looked even better if the dollar had held its value against the Euro. MandrakeSoft reports its financial results in Euros, but most of its income is in dollars. Currently, the dollar is worth about €0.82. As the company heads toward its exit from bankruptcy, Duval says it they plans to "reinforce" its business offerings. Duval said that the company's Multi-Network Firewall and Corporate Server products are doing well, and that MandrakeSoft is planning to launch a new version of the Corporate Server product soon. The company is also planning to introduce a Corporate Desktop product in the near future. There may be some growth in the near future as well. Duval noted that MandrakeSoft is planning "a few mergers, small ones to begin." Specific merger targets were not mentioned. Though there is no shortage of Linux distributions on the market, it's good to see MandrakeSoft making a healthy recovery. The company's return to profitability, without abandoning its commitment to free software, demonstrates that there is indeed money in free software for those who find the right formula.
Page editor: Jonathan Corbet Security Security news A new Adore root kit For your cracking pleasure: a new version of the Adore root kit has been announced. This code is, of course, "for educational purposes only." On the notion that it's best to look at code like this when one has downloaded it explicitly, rather than when one has found it on one's system, we grabbed a copy.Adore is a kernel module which is intended to give a cracker the full run of a compromised system without detection. To that end, it installs itself into several key parts of the kernel and lurks until somebody comes along who knows the right "key," where a key is a special process ID. If you do not know this key, finding signs of an Adore installation will be difficult, to say the least. The module starts by hooking itself into various filesystems. It digs up the inode for the root filesystem, and replaces that inode's readdir() function pointer with one of its own. The Adore version performs like the one it replaces, except that it hides any files owned by a specific user and group ID. If you are a Black Hat trying to keep installed files out of the eye of the system administrator, this is the way to do it. Similarly, Adore hooks itself into the lookup function for /proc. An attempt to read /proc/KEY, where KEY is a predefined key value, will give the current process the ability to access other Adore functions. A process which has been "authenticated" in this way can then, by accessing other special /proc filenames, give itself full root privileges or tell Adore to hide other processes from view. The module keeps a list of such processes; once a process appears in that list, it will never appear in /proc, and thus it will not by displayed by utilities like ps or top. The only way to find such processes, it would seem, would be to dig through the entire kernel task list and check to see if any of them are not represented in /proc. People who crack into systems may well want to run network services on those systems. To cater to their needs, Adore replaces the show() function for /proc/net/tcp; the new version edits out any connections involving ports that the person installing Adore would rather others didn't know about. A hidden server process, running from a hidden executable, and sitting behind a hidden port could be very hard for a system administrator to find. For good measure, Adore will also filter out entries made into files like /var/log/utmp or syslog on behalf of hidden processes. The one thing Adore does not do is hide itself; it will show up in the list of loaded kernel modules. To address that, a separate module called "cleanup" is provided. If cleanup is loaded immediately after Adore, it will patch Adore out of the list of loaded modules, thus hiding it altogether. All of this functionality has been implemented in a kernel module which is a mere 600 lines long. This module is scary; it is a living demonstration of what an attacker can do once he gets root access on a system. A careful attacker could, using this module, maintain undetected control of a compromised system indefinitely.
New vulnerabilities calife: buffer overflow
OpenSSL: denial of service vulnerabilities
samba privilege escalation
uudeview temp file problem
xitalk missing privilege release
Updated vulnerabilities PWLib: possible Denial of Service
apache2: Denial of Service vulnerability
Filename disclosure vulnerability in fam
fetchmail may crash on specially crafted message
gdk-pixbuf: buffer overflow
gtkhtml: malformed messages cause crash
iproute: local denial of service
kdelibs: cookie disclosure
kdepim: VCF file information reader vulnerability
kernel: local root exploit in 2.4.22
kernel-utils: setuid vulnerability
libpng, libpng3: buffer overflow
libxml2 - arbitrary code execution
mailman denial of service
mc: arbitrary code execution
metamail: integer and buffer overflows
mikmod: buffer overflow
mod_python: denial of service vulnerability
mozilla: multiple vulnerabilties
mpg321: format string vulnerability
mplayer: remotely exploitable buffer overflow vulnerability
mutt: buffer overflow
Nessus NASL scripting engine security issues
netpbm: insecure temporary files
openssh: timing attack leads to information disclosure
perl information leak
postfix: denial of service vulnerabilities
python: buffer overflow
sysstat: temporary file vulnerability
File overwrite vulnerability in tar and unzip
tcpdump: flaws in the ISAKMP decoding routines
Multiple vendor telnetd vulnerability
util-linux: information leak in the login program
wu-ftpd: two vulnerabilities
Resources March CRYPTO-GRAM newsletter Bruce Schneier's CRYPTO-GRAM newsletter for March is out. It looks at the "V-ID card," centralized security, and the Microsoft code leak. "Any bad guys who want the code now have it, and won't be deterred by any lawyer letter. The only thing Microsoft's lawyers are doing is preventing any good guys from looking at the code, and maybe finding vulnerabilities that Microsoft can then fix. But if you realize that Microsoft's primary fear is probably other attorneys, then their move makes sense. They want to limit the number of good guys that can access the code, because they're afraid of what might be found."
Events Computer Security Mexico 2004 Computer Security Mexico 2004 is happening May 27 and 28 in Mexico City. Click below for details and a list of keynote speakers.
Page editor: Jonathan Corbet
Kernel development Release status Kernel release status The current 2.6 prepatch is 2.6.5-rc1, which was announced by Linus on March 15. This prepatch includes the incorporation of the netpoll interface (see below), some virtual memory performance improvements, the new "kref" reference counting mechanism (see below), a big ALSA update, a new Prism54 wireless driver, an NFS update, a DMA API change (see below yet again), and many fixes. See the long-format changelog for the details.2.6.4 was released on March 10; very few fixes went in after the last release candidate. Changes since 2.6.3 include support for the Intel "ia32e" architecture, a UTF-8 tty mode, dynamic PTY allocation, sysfs support for SCSI tapes and bluetooth devices, support for large numbers of groups, a generic kernel thread infrastructure, an HFS filesystem rewrite, an R128 DRI driver security fix, the groundwork for the hotplug CPU code, and many, many fixes. The the long-format changelog has the details. Patches in Linus's BitKeeper repository include several architecture updates, a set of fixes to make the Intermezzo filesystem work again, an IDE update, asynchronous I/O support for reiserfs, and lots of fixes. The current tree from Andrew Morton is 2.6.5-rc1-mm1. Recent additions to the -mm tree include a plug-and-play subsystem update, a patch to enable 4K kernel stacks on the x86, the per-address-space block queue unplugging code (discussed here last week), an NFS update, a bunch of page cache work ("It seems to work OK here, but I suggest people not rush out and convert all of the corporate finance department's servers to 2.6.4-mm1."), and many fixes. The current 2.4 kernel is 2.4.25; Marcelo released two 2.4.26 prepatches over the last week. 2.4.26-pre3 included a fair number of architecture and networking fixes; 2.4.26-pre4 (released March 16) is a much smaller patch with just a few fixes.
Kernel development news The DMA API changes The 2.6 kernel is a stable series which, in theory, should be dedicated to the fixing of bugs rather than changing APIs. Anybody who risks thinking that things have become too stable, however, need only look at this massive patch from David Miller, which changes the DMA API and touches a full 100 files. This patch had done a little time in the -mm tree, but had never really been discussed on the mailing lists before its inclusion.The change is in the "synchronization" calls that the DMA layer provides for streaming mappings. A streaming mapping is a short-lived structure set up to support one or more direct memory access operations; depending on the architecture, setting up a streaming mapping can involve creating bounce buffers, programming I/O memory management unit (IOMMU) registers, flushing processor caches, and more. These mappings have strict rules about the "ownership" of the buffer; when a streaming mapping is created, it is owned by the device, and the processor cannot touch it. If a device driver ignores that rule, it risks corrupting data in a number of ways. It is sometimes necessary, however, to allow the processor to access a mapped streaming DMA buffer. To that end, the DMA layer has long provided a set of functions (like dma_sync_single() and pci_sync_single()) which transfer ownership of the buffer to the CPU. What has always been lacking, however, is a way to transfer ownership back to the device. To fill in that gap, the various synchronization functions have been split in two; instead of dma_sync_single() a driver must now call one or both of:
dma_sync_single_for_cpu(struct device *dev,
dma_addr_t dma_handle,
size_t size,
enum dma_data_direction direction);
dma_sync_single_for_device(struct device *dev,
dma_addr_t dma_handle,
size_t size,
enum dma_data_direction direction);
dma_sync_single_for_cpu() gives ownership of the DMA buffer back to the processor. After that call, driver code can read or modify the buffer, but the device should not touch it. A call to dma_sync_single_for_device() is required to allow the device to access the buffer again. The other synchronization functions (for scatter/gather and DAC mappings) have been changed as well. As might be expected from a change like this, the result was a lot of broken drivers. The patch fixes the in-tree users of the discontinued DMA functions. Out-of-tree and binary-only drivers, however, will have to be fixed separately.
The debut of kref When Patrick Mochel added the "kobject" type to the 2.5.45 kernel, he described it this way:
This is not meant to be fancy; just something simple for which we
can control the refcount and other common functionality using
common code.
In the 2.6 kernel, the kobject type has become, via its kset and parent pointers, the glue which holds the entire device model structure together. It is the core object implementing every entry in the sysfs virtual filesystem. Kobjects also handle the generation of hotplug events when devices come and go. Oh, yes. Kobjects also handle reference counting. The kobject type has clearly grown past its original mandate into something fairly fancy. To address the needs of kernel hackers who only want a simple reference counter, Greg Kroah-Hartman has created a new type called kref. A kref is, indeed, a simple thing:
struct kref {
atomic_t refcount;
void (*release)(struct kref *kref);
};
A kref comes with the usual functions one would expect: kref_init() to set it up, and kref_get() and kref_put() to manage the reference count. Once that count drops to zero, the release function is called to clean things up. All told, it's quite simple. In fact, it would appear to be too simple for some kernel hackers, who have questioned whether there is any need for kref at all. Why not simply manipulate a reference count directly with atomic_t operations and avoid adding the space required for the release() pointer to every reference-counted object? The answer that comes back is that buggy reference counting implementations in the kernel are far from unknown, and that the overhead of using kref is tiny. As Andrew Morton put it:
I care more about being able to say "ah, it uses kref. I
understand that refcounting idiom, I know it's well debugged and I
know that it traps common errors". That's better than "oh crap,
this thing implements its own refcounting - I need to review it for
the usual errors".
Andrew's approval is sufficient; the kref patch showed up in 2.6.5-rc1. For the future, Greg has a patch which converts the kobject reference counting mechanism over to krefs. That change may be a harder sell, however; it will expand the size of every kobject in the system (because kobjects, currently, do not store the release() function pointer directly). So that change will wait for 2.7, and may be part of a larger-scale cleanup and refactoring of the kobject type.
Lots of SCSI disks One of the motivations for increasing the size of the dev_t device number type in 2.6 was to allow the use of huge numbers of SCSI disks. In the 2.6.4 kernel, however, that promise remains unfulfilled; the SCSI subsystem makes no use of the expanded device number range. That will change in 2.6.5, however; a patch has been merged which allows the enumeration of up to 1 million SCSI disks.The authors of this patch had an interesting problem to solve: they wanted to be able to enumerate all of those disks without breaking existing systems. In other words, all of the existing SCSI device numbers have to work as they do in 2.4 and prior kernels. The solution is expressed in the following macro, which turns a device index (the "nth disk") and a partition number into its associated device number:
static unsigned int make_sd_dev(unsigned int sd_nr, unsigned int part)
{
return (part & 0xf) | ((sd_nr & 0xf) << 4) |
(sd_major((sd_nr & 0xf0) >> 4) << 20) | (sd_nr & 0xfff00);
}
LWN readers will, no doubt, immediately understand what is going on here. Your editor, however, had to stare at it for a little while. Then, as a way of avoiding doing real work, he made the following diagram to show how a device index and partition number are transmogrified into a device number.
The "remap" operation takes four bits from the device index and uses them to index into an array of the 16 major numbers which have been assigned for some time to SCSI disks: 8, 65-71, and 128-135. The lowest four bits of the device index move directly down into the minor number. The result is that the first 256 SCSI disks will get exactly the same major and minor numbers that they have in 2.4 kernels. Once that space has been exhausted, however, the four red bits in the diagram will return to zero, the major number will go back to 8, the highest-order bits in the device index are routed back into the minor number, and, as a result, the 257th disk will be given device number 8:256. The 273rd disk will advance again to the next major number; it will be given number 65:256. Additional disks will be distributed across the available major numbers indefinitely until their combined power load flips a breaker somewhere. The result is a scheme which might be a little hard for humans to follow, but, when you are dealing with thousands of disks, that will be the case anyway. Meanwhile, most of the main design goals - support lots of disks without breaking existing systems - have been met. There is one remaining issue, however: some SCSI users have been asking for the ability to have more than 15 partitions on one drive. Supporting a larger partition space and simultaneously preserving compatibility is not currently possible because the block layer expects partitions to be assigned contiguous minor numbers. Fixing that will require tweaks to the gendisk code.
Netpoll is merged One of the many new things merged into 2.6.5-rc1 is the "netpoll" infrastructure. Netpoll exists to support low-level kernel functions which may need to be able to send and receive packets over the network without involving the entire networking subsystem and without enabling interrupts. Examples include kgdbeth (which allows kernel debugging over the net), and netconsole, which enables remote, network-based consoles. The patches have been around (and in the -mm tree) for some time, but have only now found their way into the mainline. Netconsole was merged as well, but kgdbeth users will still have to apply patches for now.Supporting netconsole in network drivers turns out to be relatively easy - for most adaptors. There is a new net_device method called poll_controller(); its job is to catch up with whatever the device has been doing. For many devices, this method looks like this:
static void poll_my_card(struct net_device *dev);
{
disable_device_interrupts();
call_interrupt_handler(dev);
reenable_device_interrupts();
}
Netpoll, in other words, is simulating device interrupts from within the kernel. Some device interrupt handlers may need tweaks to ensure that they do all of the necessary work without a real hardware interrupt, but most seem to work as they are.
Which is the real software suspend? Laptop users may well have noticed that there are no less than three competing software suspend implementations for the 2.6 kernel. Two of them (pmdisk and swsusp) are in the kernel itself; the third (swsusp2) is not, but is also the implementation which has seen the most work over the last several months. Unfortunately, none of these implementations could be said to be production-level code. It is possible to make a Linux system suspend to disk and resume into something that still runs, but making it work is not yet for the faint of heart.The software suspend discussion began anew when Pavel Machek, the maintainer of the in-kernel swsusp code, asked where things were going. Pavel's preference, not surprisingly, would be to remove the pmdisk code and stick with swsusp. Pavel is not alone in feeling this way. The pmdisk implementation is a fork of the swsusp code created by Patrick Mochel, who was not enjoying good relations with Pavel at the time. By some accounts, the pmdisk code is better, but it suffers from a major problem: Patrick has gotten a new job and has vanished from the kernel development world. As a result, pmdisk has seen no development work for several months, and it is a rare user who can make it work reliably. Unless Patrick surfaces and starts working on the code again, it is likely to go away fairly soon. The real question is what to do about swsusp2. This version of the suspend code has seen significant work by Nigel Cunningham and others. It has a number of features that others lack: the ability to abort a suspend operation, a "nice display," compression of the saved image (which can speed suspends and resumes on systems with slow disks), etc. The real difference, though, is that swsusp2 is, for many people, the only version that works at all reliably. So there is some real desire to see the swsusp2 work merged into 2.6, and further development efforts concentrated there. The hangup seems to be the fact that the swsusp2 patch is large, and it touches a great many core files. Many of those changes are aimed at making the "refrigerator" work better. Before a system can be suspended, all processes must be put into a quiet, known state. This works by setting a "freeze" flag and sending a signal to every process telling it to put itself into the refrigerator. Once all processes are nicely chilled, the system can save its state and suspend itself. Processes will not refrigerate themselves immediately; they must first get to a point where they hold no important resources. Sometimes, a process must get something from another process before it can be refrigerated; the example that is often raised is a process waiting for a response from an NFS server process. If the NFS server is refrigerated first, the other process will never get to where it can be frozen, and the suspend operation will fail. To avoid this sort of situation, the swsusp2 developers have gone to great lengths to identify places where a process should not, yet, be refrigerated. The result is a great many macros with names like SWSUSP_ACTIVITY_STARTING s | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||